hass-core/homeassistant/components/evohome.py
zxdavb 8d65230a36 Add (EU-based) Honeywell evohome CH/DHW controller (#16427)
* Add support for Honeywell evohome CH/DHW systems

More flake8 corrections

Passes Flake8 tests

Almost passed flake8.pylint!

Passed all tox tests

Now it needs serious testing!

Almost ready to submit

BUGFIX: DHW state now functional

More improvements to available()

Solved the DHW temp units problem!

Last minute bug squash

to improve dicts merge

Trying to rebase

fixing more rbase errors

revert to creating HTTP_error_code internally for now

ready to submit PR

Added support for Honeywell evohome CH/DHW systems

* Updated requirements_test_all.txt

* Fix: D401 First line should be in imperative mood

* Remove _LOGGER.info (replace with _LOGGER.debug)

* raise PlatformNotReady when RequestException during setup()

* Revert some LOGGER.debug to LOGGER.warning

* Improved logging levels, and removed some unwanted comments

* Improvments to logging - additional self._status info

* BUGFIX: DHW wrongly showing available = False (and some other tweaks)

* Fix trailing whitespace

* Remove state_attributes override and API_VER code

* Removed heating zones, DHW and heuristics to reduce # lines of code

* Removed some more lines of code

* Removed unused configuration parameters

* Remove some more un-needed lines

* Removed more (uneeded) lines of code & fixed two minor typos

* Improvements to debug logging of available() = False

* Improvements to code, and code clean-up

* Corrected a minor typo

* A small tidy up

* reduces precision of emulated temps floats to 0.1

* Some code improvements as suggested by JeardM

* Rewrite of exception handler

* Removed another unwanted logging in properties

* Remove async_ version of various methods

* moved available heuristics to update()

* Cleanup of code, and re-work linter hints

* fixed a minor documentation typo

* scan_interval is now no longer a configurable option

* Change from Master/Slave to Parent/Child

* Removed the last of the slaves

* Removed the last of the masters

* Move -PARALLEL_UPDATES to .\climate\evohome.py'

* main code moved to climate/evohome.py

* merge EvoEntity into EvoController class

* remove should_poll (for now)

* woops! left a hint in

* removed icon

* only log a WARNING the first time available = False

* cleanup dodgy exception handling

* Tidy up exception handling

* Many changes as suggested by @MartinHjelmare, thanks

* remove hass from init, part 1

* use async_added_to_hass instead of dispatcher_connect

* remove hass from init, part 2 (done)

* add 1-2 arrays, and tidied up some comments

* from dispatcher to async_added_to_hass

* cleaned up some logging, and removed others

* Many changes as request by @MartinHjelmare

* Homage to the lint

* Changed to the HA of doing operating_mode

* Now using update_before_add=True

* reduced logging further still...

* fixed minor lint

* fix a small logic error

* Add device_state_attributes to track actual operating mode

* Clean up doc quotes caused by  previous changes

* Woops! removed some debug lines that shoudln't have stayed in

* Add a complete set of device_state_attributes

* Cleanup some constants

* Remove more legacy code

* domain_data to evo_data & this else should be a finally

* minor change for readability

* Minor change for readability #2

* removed unused code

* small tidy up - no code changes

* fix minor lint

* Correct URLs & descriptions in docstring

* whoops - fixed a typo in docstrings

* remove an unused line of cide & a small tidy-up
2018-09-27 13:29:44 +02:00

145 lines
4.5 KiB
Python

"""Support for Honeywell evohome (EMEA/EU-based systems only).
Support for a temperature control system (TCS, controller) with 0+ heating
zones (e.g. TRVs, relays) and, optionally, a DHW controller.
For more details about this component, please refer to the documentation at
https://home-assistant.io/components/evohome/
"""
# Glossary:
# TCS - temperature control system (a.k.a. Controller, Parent), which can
# have up to 13 Children:
# 0-12 Heating zones (a.k.a. Zone), and
# 0-1 DHW controller, (a.k.a. Boiler)
import logging
from requests.exceptions import HTTPError
import voluptuous as vol
from homeassistant.const import (
CONF_USERNAME,
CONF_PASSWORD,
CONF_SCAN_INTERVAL,
HTTP_BAD_REQUEST
)
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.discovery import load_platform
REQUIREMENTS = ['evohomeclient==0.2.7']
# If ever > 0.2.7, re-check the work-around wrapper is still required when
# instantiating the client, below.
_LOGGER = logging.getLogger(__name__)
DOMAIN = 'evohome'
DATA_EVOHOME = 'data_' + DOMAIN
CONF_LOCATION_IDX = 'location_idx'
MAX_TEMP = 28
MIN_TEMP = 5
SCAN_INTERVAL_DEFAULT = 180
SCAN_INTERVAL_MAX = 300
CONFIG_SCHEMA = vol.Schema({
DOMAIN: vol.Schema({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_LOCATION_IDX, default=0): cv.positive_int,
}),
}, extra=vol.ALLOW_EXTRA)
# These are used to help prevent E501 (line too long) violations.
GWS = 'gateways'
TCS = 'temperatureControlSystems'
def setup(hass, config):
"""Create a Honeywell (EMEA/EU) evohome CH/DHW system.
One controller with 0+ heating zones (e.g. TRVs, relays) and, optionally, a
DHW controller. Does not work for US-based systems.
"""
evo_data = hass.data[DATA_EVOHOME] = {}
evo_data['timers'] = {}
evo_data['params'] = dict(config[DOMAIN])
evo_data['params'][CONF_SCAN_INTERVAL] = SCAN_INTERVAL_DEFAULT
from evohomeclient2 import EvohomeClient
_LOGGER.debug("setup(): API call [4 request(s)]: client.__init__()...")
try:
# There's a bug in evohomeclient2 v0.2.7: the client.__init__() sets
# the root loglevel when EvohomeClient(debug=?), so remember it now...
log_level = logging.getLogger().getEffectiveLevel()
client = EvohomeClient(
evo_data['params'][CONF_USERNAME],
evo_data['params'][CONF_PASSWORD],
debug=False
)
# ...then restore it to what it was before instantiating the client
logging.getLogger().setLevel(log_level)
except HTTPError as err:
if err.response.status_code == HTTP_BAD_REQUEST:
_LOGGER.error(
"Failed to establish a connection with evohome web servers, "
"Check your username (%s), and password are correct."
"Unable to continue. Resolve any errors and restart HA.",
evo_data['params'][CONF_USERNAME]
)
return False # unable to continue
raise # we dont handle any other HTTPErrors
finally: # Redact username, password as no longer needed.
evo_data['params'][CONF_USERNAME] = 'REDACTED'
evo_data['params'][CONF_PASSWORD] = 'REDACTED'
evo_data['client'] = client
# Redact any installation data we'll never need.
if client.installation_info[0]['locationInfo']['locationId'] != 'REDACTED':
for loc in client.installation_info:
loc['locationInfo']['streetAddress'] = 'REDACTED'
loc['locationInfo']['city'] = 'REDACTED'
loc['locationInfo']['locationOwner'] = 'REDACTED'
loc[GWS][0]['gatewayInfo'] = 'REDACTED'
# 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
)
return False # unable to continue
evo_data['status'] = {}
if _LOGGER.isEnabledFor(logging.DEBUG):
tmp_loc = dict(evo_data['config'])
tmp_loc['locationInfo']['postcode'] = 'REDACTED'
tmp_tcs = tmp_loc[GWS][0][TCS][0]
if 'zones' in tmp_tcs:
tmp_tcs['zones'] = '...'
if 'dhw' in tmp_tcs:
tmp_tcs['dhw'] = '...'
_LOGGER.debug("setup(), location = %s", tmp_loc)
load_platform(hass, 'climate', DOMAIN)
return True