mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +00:00
Refactor evohome to prepare for water_heater (#23489)
* refactor - add const.py, change order of propertys, methods * import client at top of file * remove debug line * de-lint * delint * add me as CODEOWNER * remove lint hint * delint
This commit is contained in:
parent
3338f5c9b4
commit
6130831a43
@ -73,6 +73,7 @@ homeassistant/components/epsonworkforce/* @ThaStealth
|
|||||||
homeassistant/components/eq3btsmart/* @rytilahti
|
homeassistant/components/eq3btsmart/* @rytilahti
|
||||||
homeassistant/components/esphome/* @OttoWinter
|
homeassistant/components/esphome/* @OttoWinter
|
||||||
homeassistant/components/essent/* @TheLastProject
|
homeassistant/components/essent/* @TheLastProject
|
||||||
|
homeassistant/components/evohome/* @zxdavb
|
||||||
homeassistant/components/file/* @fabaff
|
homeassistant/components/file/* @fabaff
|
||||||
homeassistant/components/filter/* @dgomes
|
homeassistant/components/filter/* @dgomes
|
||||||
homeassistant/components/fitbit/* @robbiet480
|
homeassistant/components/fitbit/* @robbiet480
|
||||||
|
@ -5,26 +5,31 @@
|
|||||||
# 0-12 Heating zones (a.k.a. Zone), and
|
# 0-12 Heating zones (a.k.a. Zone), and
|
||||||
# 0-1 DHW controller, (a.k.a. Boiler)
|
# 0-1 DHW controller, (a.k.a. Boiler)
|
||||||
# The TCS & Zones are implemented as Climate devices, Boiler as a WaterHeater
|
# The TCS & Zones are implemented as Climate devices, Boiler as a WaterHeater
|
||||||
from datetime import timedelta
|
from datetime import datetime, timedelta
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import requests.exceptions
|
import requests.exceptions
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import evohomeclient2
|
||||||
|
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_SCAN_INTERVAL, CONF_USERNAME, CONF_PASSWORD,
|
CONF_SCAN_INTERVAL, CONF_USERNAME, CONF_PASSWORD,
|
||||||
EVENT_HOMEASSISTANT_START)
|
EVENT_HOMEASSISTANT_START,
|
||||||
|
HTTP_SERVICE_UNAVAILABLE, HTTP_TOO_MANY_REQUESTS,
|
||||||
|
PRECISION_HALVES, TEMP_CELSIUS)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.discovery import load_platform
|
from homeassistant.helpers.discovery import load_platform
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
from homeassistant.helpers.dispatcher import (
|
||||||
|
async_dispatcher_connect, async_dispatcher_send)
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
DOMAIN, DATA_EVOHOME, DISPATCHER_EVOHOME, GWS, TCS)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DOMAIN = 'evohome'
|
|
||||||
DATA_EVOHOME = 'data_' + DOMAIN
|
|
||||||
DISPATCHER_EVOHOME = 'dispatcher_' + DOMAIN
|
|
||||||
|
|
||||||
CONF_LOCATION_IDX = 'location_idx'
|
CONF_LOCATION_IDX = 'location_idx'
|
||||||
SCAN_INTERVAL_DEFAULT = timedelta(seconds=300)
|
SCAN_INTERVAL_DEFAULT = timedelta(seconds=300)
|
||||||
SCAN_INTERVAL_MINIMUM = timedelta(seconds=180)
|
SCAN_INTERVAL_MINIMUM = timedelta(seconds=180)
|
||||||
@ -43,10 +48,6 @@ CONF_SECRETS = [
|
|||||||
CONF_USERNAME, CONF_PASSWORD,
|
CONF_USERNAME, CONF_PASSWORD,
|
||||||
]
|
]
|
||||||
|
|
||||||
# These are used to help prevent E501 (line too long) violations.
|
|
||||||
GWS = 'gateways'
|
|
||||||
TCS = 'temperatureControlSystems'
|
|
||||||
|
|
||||||
# bit masks for dispatcher packets
|
# bit masks for dispatcher packets
|
||||||
EVO_PARENT = 0x01
|
EVO_PARENT = 0x01
|
||||||
EVO_CHILD = 0x02
|
EVO_CHILD = 0x02
|
||||||
@ -66,8 +67,6 @@ def setup(hass, hass_config):
|
|||||||
scan_interval = timedelta(
|
scan_interval = timedelta(
|
||||||
minutes=(scan_interval.total_seconds() + 59) // 60)
|
minutes=(scan_interval.total_seconds() + 59) // 60)
|
||||||
|
|
||||||
import evohomeclient2
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client = evo_data['client'] = evohomeclient2.EvohomeClient(
|
client = evo_data['client'] = evohomeclient2.EvohomeClient(
|
||||||
evo_data['params'][CONF_USERNAME],
|
evo_data['params'][CONF_USERNAME],
|
||||||
@ -145,3 +144,129 @@ def setup(hass, hass_config):
|
|||||||
hass.bus.listen(EVENT_HOMEASSISTANT_START, _first_update)
|
hass.bus.listen(EVENT_HOMEASSISTANT_START, _first_update)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class EvoDevice(Entity):
|
||||||
|
"""Base for any Honeywell evohome device.
|
||||||
|
|
||||||
|
Such devices include the Controller, (up to 12) Heating Zones and
|
||||||
|
(optionally) a DHW controller.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, evo_data, client, obj_ref):
|
||||||
|
"""Initialize the evohome entity."""
|
||||||
|
self._client = client
|
||||||
|
self._obj = obj_ref
|
||||||
|
|
||||||
|
self._name = None
|
||||||
|
self._icon = None
|
||||||
|
self._type = None
|
||||||
|
|
||||||
|
self._supported_features = None
|
||||||
|
self._operation_list = None
|
||||||
|
|
||||||
|
self._params = evo_data['params']
|
||||||
|
self._timers = evo_data['timers']
|
||||||
|
self._status = {}
|
||||||
|
|
||||||
|
self._available = False # should become True after first update()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _connect(self, packet):
|
||||||
|
if packet['to'] & self._type and packet['signal'] == 'refresh':
|
||||||
|
self.async_schedule_update_ha_state(force_refresh=True)
|
||||||
|
|
||||||
|
def _handle_exception(self, err):
|
||||||
|
try:
|
||||||
|
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(
|
||||||
|
"Unable to connect with the vendor's server. "
|
||||||
|
"Check your network and the vendor's status page."
|
||||||
|
)
|
||||||
|
|
||||||
|
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."
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# These properties, methods are from the Entity class
|
||||||
|
async def async_added_to_hass(self):
|
||||||
|
"""Run when entity about to be added."""
|
||||||
|
async_dispatcher_connect(self.hass, DISPATCHER_EVOHOME, self._connect)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self) -> bool:
|
||||||
|
"""Most evohome devices push their state to HA.
|
||||||
|
|
||||||
|
Only the Controller should be polled.
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
"""Return the name to use in the frontend UI."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
"""Return the device state attributes of the evohome device.
|
||||||
|
|
||||||
|
This is state data that is not available otherwise, due to the
|
||||||
|
restrictions placed upon ClimateDevice properties, etc. by HA.
|
||||||
|
"""
|
||||||
|
return {'status': self._status}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def icon(self):
|
||||||
|
"""Return the icon to use in the frontend UI."""
|
||||||
|
return self._icon
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
"""Return True if the device is currently available."""
|
||||||
|
return self._available
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_features(self):
|
||||||
|
"""Get the list of supported features of the device."""
|
||||||
|
return self._supported_features
|
||||||
|
|
||||||
|
# These properties are common to ClimateDevice, WaterHeaterDevice classes
|
||||||
|
@property
|
||||||
|
def precision(self):
|
||||||
|
"""Return the temperature precision to use in the frontend UI."""
|
||||||
|
return PRECISION_HALVES
|
||||||
|
|
||||||
|
@property
|
||||||
|
def temperature_unit(self):
|
||||||
|
"""Return the temperature unit to use in the frontend UI."""
|
||||||
|
return TEMP_CELSIUS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def operation_list(self):
|
||||||
|
"""Return the list of available operations."""
|
||||||
|
return self._operation_list
|
||||||
|
@ -4,20 +4,21 @@ import logging
|
|||||||
|
|
||||||
import requests.exceptions
|
import requests.exceptions
|
||||||
|
|
||||||
|
import evohomeclient2
|
||||||
|
|
||||||
from homeassistant.components.climate import ClimateDevice
|
from homeassistant.components.climate import ClimateDevice
|
||||||
from homeassistant.components.climate.const import (
|
from homeassistant.components.climate.const import (
|
||||||
STATE_AUTO, STATE_ECO, STATE_MANUAL, SUPPORT_AWAY_MODE, SUPPORT_ON_OFF,
|
STATE_AUTO, STATE_ECO, STATE_MANUAL, SUPPORT_AWAY_MODE, SUPPORT_ON_OFF,
|
||||||
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE)
|
SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_SCAN_INTERVAL, HTTP_SERVICE_UNAVAILABLE, HTTP_TOO_MANY_REQUESTS,
|
CONF_SCAN_INTERVAL, STATE_OFF,)
|
||||||
PRECISION_HALVES, STATE_OFF, TEMP_CELSIUS)
|
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||||
from homeassistant.core import callback
|
|
||||||
from homeassistant.helpers.dispatcher import (
|
|
||||||
async_dispatcher_connect, dispatcher_send)
|
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
CONF_LOCATION_IDX, DATA_EVOHOME, DISPATCHER_EVOHOME, EVO_CHILD, EVO_PARENT,
|
EvoDevice,
|
||||||
GWS, TCS)
|
CONF_LOCATION_IDX, EVO_CHILD, EVO_PARENT)
|
||||||
|
from .const import (
|
||||||
|
DATA_EVOHOME, DISPATCHER_EVOHOME, GWS, TCS)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -103,115 +104,7 @@ async def async_setup_platform(hass, hass_config, async_add_entities,
|
|||||||
async_add_entities(entities, update_before_add=False)
|
async_add_entities(entities, update_before_add=False)
|
||||||
|
|
||||||
|
|
||||||
class EvoClimateDevice(ClimateDevice):
|
class EvoZone(EvoDevice, ClimateDevice):
|
||||||
"""Base for a Honeywell evohome Climate device."""
|
|
||||||
|
|
||||||
# pylint: disable=no-member
|
|
||||||
|
|
||||||
def __init__(self, evo_data, client, obj_ref):
|
|
||||||
"""Initialize the evohome entity."""
|
|
||||||
self._client = client
|
|
||||||
self._obj = obj_ref
|
|
||||||
|
|
||||||
self._params = evo_data['params']
|
|
||||||
self._timers = evo_data['timers']
|
|
||||||
self._status = {}
|
|
||||||
|
|
||||||
self._available = False # should become True after first update()
|
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
|
||||||
"""Run when entity about to be added."""
|
|
||||||
async_dispatcher_connect(self.hass, DISPATCHER_EVOHOME, self._connect)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def _connect(self, packet):
|
|
||||||
if packet['to'] & self._type and packet['signal'] == 'refresh':
|
|
||||||
self.async_schedule_update_ha_state(force_refresh=True)
|
|
||||||
|
|
||||||
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(
|
|
||||||
"Unable to connect with the vendor's server. "
|
|
||||||
"Check your network and the vendor's status page."
|
|
||||||
)
|
|
||||||
|
|
||||||
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."
|
|
||||||
)
|
|
||||||
|
|
||||||
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:
|
|
||||||
"""Return the name to use in the frontend UI."""
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
@property
|
|
||||||
def icon(self):
|
|
||||||
"""Return the icon to use in the frontend UI."""
|
|
||||||
return self._icon
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_state_attributes(self):
|
|
||||||
"""Return the device state attributes of the evohome Climate device.
|
|
||||||
|
|
||||||
This is state data that is not available otherwise, due to the
|
|
||||||
restrictions placed upon ClimateDevice properties, etc. by HA.
|
|
||||||
"""
|
|
||||||
return {'status': self._status}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def available(self) -> bool:
|
|
||||||
"""Return True if the device is currently available."""
|
|
||||||
return self._available
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self):
|
|
||||||
"""Get the list of supported features of the device."""
|
|
||||||
return self._supported_features
|
|
||||||
|
|
||||||
@property
|
|
||||||
def operation_list(self):
|
|
||||||
"""Return the list of available operations."""
|
|
||||||
return self._operation_list
|
|
||||||
|
|
||||||
@property
|
|
||||||
def temperature_unit(self):
|
|
||||||
"""Return the temperature unit to use in the frontend UI."""
|
|
||||||
return TEMP_CELSIUS
|
|
||||||
|
|
||||||
@property
|
|
||||||
def precision(self):
|
|
||||||
"""Return the temperature precision to use in the frontend UI."""
|
|
||||||
return PRECISION_HALVES
|
|
||||||
|
|
||||||
|
|
||||||
class EvoZone(EvoClimateDevice):
|
|
||||||
"""Base for a Honeywell evohome Zone device."""
|
"""Base for a Honeywell evohome Zone device."""
|
||||||
|
|
||||||
def __init__(self, evo_data, client, obj_ref):
|
def __init__(self, evo_data, client, obj_ref):
|
||||||
@ -235,33 +128,6 @@ class EvoZone(EvoClimateDevice):
|
|||||||
SUPPORT_TARGET_TEMPERATURE | \
|
SUPPORT_TARGET_TEMPERATURE | \
|
||||||
SUPPORT_ON_OFF
|
SUPPORT_ON_OFF
|
||||||
|
|
||||||
@property
|
|
||||||
def min_temp(self):
|
|
||||||
"""Return the minimum target temperature of a evohome Zone.
|
|
||||||
|
|
||||||
The default is 5 (in Celsius), but it is configurable within 5-35.
|
|
||||||
"""
|
|
||||||
return self._config['setpointCapabilities']['minHeatSetpoint']
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_temp(self):
|
|
||||||
"""Return the minimum target temperature of a evohome Zone.
|
|
||||||
|
|
||||||
The default is 35 (in Celsius), but it is configurable within 5-35.
|
|
||||||
"""
|
|
||||||
return self._config['setpointCapabilities']['maxHeatSetpoint']
|
|
||||||
|
|
||||||
@property
|
|
||||||
def target_temperature(self):
|
|
||||||
"""Return the target temperature of the evohome Zone."""
|
|
||||||
return self._status['setpointStatus']['targetHeatTemperature']
|
|
||||||
|
|
||||||
@property
|
|
||||||
def current_temperature(self):
|
|
||||||
"""Return the current temperature of the evohome Zone."""
|
|
||||||
return (self._status['temperatureStatus']['temperature']
|
|
||||||
if self._status['temperatureStatus']['isAvailable'] else None)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_operation(self):
|
def current_operation(self):
|
||||||
"""Return the current operating mode of the evohome Zone.
|
"""Return the current operating mode of the evohome Zone.
|
||||||
@ -285,6 +151,17 @@ class EvoZone(EvoClimateDevice):
|
|||||||
|
|
||||||
return current_operation
|
return current_operation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_temperature(self):
|
||||||
|
"""Return the current temperature of the evohome Zone."""
|
||||||
|
return (self._status['temperatureStatus']['temperature']
|
||||||
|
if self._status['temperatureStatus']['isAvailable'] else None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_temperature(self):
|
||||||
|
"""Return the target temperature of the evohome Zone."""
|
||||||
|
return self._status['setpointStatus']['targetHeatTemperature']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return True if the evohome Zone is off.
|
"""Return True if the evohome Zone is off.
|
||||||
@ -297,6 +174,22 @@ class EvoZone(EvoClimateDevice):
|
|||||||
self._status['setpointStatus']['setpointMode'] == EVO_PERMOVER
|
self._status['setpointStatus']['setpointMode'] == EVO_PERMOVER
|
||||||
return not is_off
|
return not is_off
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_temp(self):
|
||||||
|
"""Return the minimum target temperature of a evohome Zone.
|
||||||
|
|
||||||
|
The default is 5 (in Celsius), but it is configurable within 5-35.
|
||||||
|
"""
|
||||||
|
return self._config['setpointCapabilities']['minHeatSetpoint']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_temp(self):
|
||||||
|
"""Return the maximum target temperature of a evohome Zone.
|
||||||
|
|
||||||
|
The default is 35 (in Celsius), but it is configurable within 5-35.
|
||||||
|
"""
|
||||||
|
return self._config['setpointCapabilities']['maxHeatSetpoint']
|
||||||
|
|
||||||
def _set_temperature(self, temperature, until=None):
|
def _set_temperature(self, temperature, until=None):
|
||||||
"""Set the new target temperature of a Zone.
|
"""Set the new target temperature of a Zone.
|
||||||
|
|
||||||
@ -305,7 +198,6 @@ class EvoZone(EvoClimateDevice):
|
|||||||
- None for PermanentOverride (i.e. indefinitely)
|
- None for PermanentOverride (i.e. indefinitely)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
import evohomeclient2
|
|
||||||
self._obj.set_temperature(temperature, until)
|
self._obj.set_temperature(temperature, until)
|
||||||
except (requests.exceptions.RequestException,
|
except (requests.exceptions.RequestException,
|
||||||
evohomeclient2.AuthenticationError) as err:
|
evohomeclient2.AuthenticationError) as err:
|
||||||
@ -330,6 +222,29 @@ class EvoZone(EvoClimateDevice):
|
|||||||
"""
|
"""
|
||||||
self._set_temperature(self.min_temp, until=None)
|
self._set_temperature(self.min_temp, until=None)
|
||||||
|
|
||||||
|
def _set_operation_mode(self, operation_mode):
|
||||||
|
if operation_mode == EVO_FOLLOW:
|
||||||
|
try:
|
||||||
|
self._obj.cancel_temp_override()
|
||||||
|
except (requests.exceptions.RequestException,
|
||||||
|
evohomeclient2.AuthenticationError) as err:
|
||||||
|
self._handle_exception(err)
|
||||||
|
|
||||||
|
elif operation_mode == EVO_TEMPOVER:
|
||||||
|
_LOGGER.error(
|
||||||
|
"_set_operation_mode(op_mode=%s): mode not yet implemented",
|
||||||
|
operation_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
elif operation_mode == EVO_PERMOVER:
|
||||||
|
self._set_temperature(self.target_temperature, until=None)
|
||||||
|
|
||||||
|
else:
|
||||||
|
_LOGGER.error(
|
||||||
|
"_set_operation_mode(op_mode=%s): mode not valid",
|
||||||
|
operation_mode
|
||||||
|
)
|
||||||
|
|
||||||
def set_operation_mode(self, operation_mode):
|
def set_operation_mode(self, operation_mode):
|
||||||
"""Set an operating mode for a Zone.
|
"""Set an operating mode for a Zone.
|
||||||
|
|
||||||
@ -354,38 +269,6 @@ class EvoZone(EvoClimateDevice):
|
|||||||
"""
|
"""
|
||||||
self._set_operation_mode(HA_STATE_TO_ZONE.get(operation_mode))
|
self._set_operation_mode(HA_STATE_TO_ZONE.get(operation_mode))
|
||||||
|
|
||||||
def _set_operation_mode(self, operation_mode):
|
|
||||||
if operation_mode == EVO_FOLLOW:
|
|
||||||
try:
|
|
||||||
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(
|
|
||||||
"_set_operation_mode(op_mode=%s): mode not yet implemented",
|
|
||||||
operation_mode
|
|
||||||
)
|
|
||||||
|
|
||||||
elif operation_mode == EVO_PERMOVER:
|
|
||||||
self._set_temperature(self.target_temperature, until=None)
|
|
||||||
|
|
||||||
else:
|
|
||||||
_LOGGER.error(
|
|
||||||
"_set_operation_mode(op_mode=%s): mode not valid",
|
|
||||||
operation_mode
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def should_poll(self) -> bool:
|
|
||||||
"""Return False as evohome child devices should never be polled.
|
|
||||||
|
|
||||||
The evohome Controller will inform its children when to update().
|
|
||||||
"""
|
|
||||||
return False
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Process the evohome Zone's state data."""
|
"""Process the evohome Zone's state data."""
|
||||||
evo_data = self.hass.data[DATA_EVOHOME]
|
evo_data = self.hass.data[DATA_EVOHOME]
|
||||||
@ -398,7 +281,7 @@ class EvoZone(EvoClimateDevice):
|
|||||||
self._available = True
|
self._available = True
|
||||||
|
|
||||||
|
|
||||||
class EvoController(EvoClimateDevice):
|
class EvoController(EvoDevice, ClimateDevice):
|
||||||
"""Base for a Honeywell evohome hub/Controller device.
|
"""Base for a Honeywell evohome hub/Controller device.
|
||||||
|
|
||||||
The Controller (aka TCS, temperature control system) is the parent of all
|
The Controller (aka TCS, temperature control system) is the parent of all
|
||||||
@ -445,22 +328,18 @@ class EvoController(EvoClimateDevice):
|
|||||||
return TCS_STATE_TO_HA.get(self._status['systemModeStatus']['mode'])
|
return TCS_STATE_TO_HA.get(self._status['systemModeStatus']['mode'])
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def min_temp(self):
|
def current_temperature(self):
|
||||||
"""Return the minimum target temperature of a evohome Controller.
|
"""Return the average current temperature of the Heating/DHW zones.
|
||||||
|
|
||||||
Although evohome Controllers do not have a minimum target temp, one is
|
Although evohome Controllers do not have a target temp, one is
|
||||||
expected by the HA schema; the default for an evohome HR92 is used.
|
expected by the HA schema.
|
||||||
"""
|
"""
|
||||||
return 5
|
tmp_list = [x for x in self._status['zones']
|
||||||
|
if x['temperatureStatus']['isAvailable']]
|
||||||
|
temps = [zone['temperatureStatus']['temperature'] for zone in tmp_list]
|
||||||
|
|
||||||
@property
|
avg_temp = round(sum(temps) / len(temps), 1) if temps else None
|
||||||
def max_temp(self):
|
return avg_temp
|
||||||
"""Return the minimum target temperature of a evohome Controller.
|
|
||||||
|
|
||||||
Although evohome Controllers do not have a maximum target temp, one is
|
|
||||||
expected by the HA schema; the default for an evohome HR92 is used.
|
|
||||||
"""
|
|
||||||
return 35
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def target_temperature(self):
|
def target_temperature(self):
|
||||||
@ -476,18 +355,9 @@ class EvoController(EvoClimateDevice):
|
|||||||
return avg_temp
|
return avg_temp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_temperature(self):
|
def is_away_mode_on(self) -> bool:
|
||||||
"""Return the average current temperature of the Heating/DHW zones.
|
"""Return True if away mode is on."""
|
||||||
|
return self._status['systemModeStatus']['mode'] == EVO_AWAY
|
||||||
Although evohome Controllers do not have a target temp, one is
|
|
||||||
expected by the HA schema.
|
|
||||||
"""
|
|
||||||
tmp_list = [x for x in self._status['zones']
|
|
||||||
if x['temperatureStatus']['isAvailable'] is True]
|
|
||||||
temps = [zone['temperatureStatus']['temperature'] for zone in tmp_list]
|
|
||||||
|
|
||||||
avg_temp = round(sum(temps) / len(temps), 1) if temps else None
|
|
||||||
return avg_temp
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
@ -499,9 +369,42 @@ class EvoController(EvoClimateDevice):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_away_mode_on(self) -> bool:
|
def min_temp(self):
|
||||||
"""Return True if away mode is on."""
|
"""Return the minimum target temperature of a evohome Controller.
|
||||||
return self._status['systemModeStatus']['mode'] == EVO_AWAY
|
|
||||||
|
Although evohome Controllers do not have a minimum target temp, one is
|
||||||
|
expected by the HA schema; the default for an evohome HR92 is used.
|
||||||
|
"""
|
||||||
|
return 5
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_temp(self):
|
||||||
|
"""Return the maximum target temperature of a evohome Controller.
|
||||||
|
|
||||||
|
Although evohome Controllers do not have a maximum target temp, one is
|
||||||
|
expected by the HA schema; the default for an evohome HR92 is used.
|
||||||
|
"""
|
||||||
|
return 35
|
||||||
|
|
||||||
|
@property
|
||||||
|
def should_poll(self) -> bool:
|
||||||
|
"""Return True as the evohome Controller should always be polled."""
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _set_operation_mode(self, operation_mode):
|
||||||
|
try:
|
||||||
|
self._obj._set_status(operation_mode) # noqa: E501; pylint: disable=protected-access
|
||||||
|
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.
|
||||||
|
|
||||||
|
Currently limited to 'Auto', 'AutoWithEco' & 'HeatingOff'. If 'Away'
|
||||||
|
mode is needed, it can be enabled via turn_away_mode_on method.
|
||||||
|
"""
|
||||||
|
self._set_operation_mode(HA_STATE_TO_TCS.get(operation_mode))
|
||||||
|
|
||||||
def turn_away_mode_on(self):
|
def turn_away_mode_on(self):
|
||||||
"""Turn away mode on.
|
"""Turn away mode on.
|
||||||
@ -519,27 +422,6 @@ class EvoController(EvoClimateDevice):
|
|||||||
"""
|
"""
|
||||||
self._set_operation_mode(EVO_AUTO)
|
self._set_operation_mode(EVO_AUTO)
|
||||||
|
|
||||||
def _set_operation_mode(self, operation_mode):
|
|
||||||
try:
|
|
||||||
import evohomeclient2
|
|
||||||
self._obj._set_status(operation_mode) # noqa: E501; pylint: disable=protected-access
|
|
||||||
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.
|
|
||||||
|
|
||||||
Currently limited to 'Auto', 'AutoWithEco' & 'HeatingOff'. If 'Away'
|
|
||||||
mode is needed, it can be enabled via turn_away_mode_on method.
|
|
||||||
"""
|
|
||||||
self._set_operation_mode(HA_STATE_TO_TCS.get(operation_mode))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def should_poll(self) -> bool:
|
|
||||||
"""Return True as the evohome Controller should always be polled."""
|
|
||||||
return True
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Get the latest state data of the entire evohome Location.
|
"""Get the latest state data of the entire evohome Location.
|
||||||
|
|
||||||
@ -559,7 +441,6 @@ class EvoController(EvoClimateDevice):
|
|||||||
loc_idx = self._params[CONF_LOCATION_IDX]
|
loc_idx = self._params[CONF_LOCATION_IDX]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import evohomeclient2
|
|
||||||
self._status.update(
|
self._status.update(
|
||||||
self._client.locations[loc_idx].status()[GWS][0][TCS][0])
|
self._client.locations[loc_idx].status()[GWS][0][TCS][0])
|
||||||
except (requests.exceptions.RequestException,
|
except (requests.exceptions.RequestException,
|
||||||
|
9
homeassistant/components/evohome/const.py
Normal file
9
homeassistant/components/evohome/const.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"""Provides the constants needed for evohome."""
|
||||||
|
|
||||||
|
DOMAIN = 'evohome'
|
||||||
|
DATA_EVOHOME = 'data_' + DOMAIN
|
||||||
|
DISPATCHER_EVOHOME = 'dispatcher_' + DOMAIN
|
||||||
|
|
||||||
|
# These are used only to help prevent E501 (line too long) violations.
|
||||||
|
GWS = 'gateways'
|
||||||
|
TCS = 'temperatureControlSystems'
|
@ -6,5 +6,5 @@
|
|||||||
"evohomeclient==0.3.2"
|
"evohomeclient==0.3.2"
|
||||||
],
|
],
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": []
|
"codeowners": ["@zxdavb"]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user