mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
Add Honeywell US thermostat support
This adds support for the US variant of the Honeywell connected thermostat. The interface is super simple, so this doesn't add any external dependencies. It supports basic temperature, setpoint, and control. Issue #998 notes that the existing honeywell module doesn't work for US models, which is because they are totally different. In order to indicate to the honeywell platform module that the thermostat is a US-type, we key off of whether or not the thermostat id is provided. This is something that US people have (and require to identify one of potentially multiple thermostats in their account) and EU people will not.
This commit is contained in:
parent
2ba237eac8
commit
062fe79b3f
@ -9,33 +9,32 @@ https://home-assistant.io/components/thermostat.honeywell/
|
|||||||
import logging
|
import logging
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from homeassistant.components.thermostat import ThermostatDevice
|
from homeassistant.components.thermostat import ThermostatDevice
|
||||||
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS)
|
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD, TEMP_CELCIUS,
|
||||||
|
TEMP_FAHRENHEIT)
|
||||||
|
|
||||||
REQUIREMENTS = ['evohomeclient==0.2.4']
|
REQUIREMENTS = ['evohomeclient==0.2.4']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF_AWAY_TEMP = "away_temperature"
|
CONF_AWAY_TEMP = "away_temperature"
|
||||||
|
US_SYSTEM_SWITCH_POSITIONS = {1: 'Heat',
|
||||||
|
2: 'Off',
|
||||||
|
3: 'Cool'}
|
||||||
|
US_BASEURL = 'https://mytotalconnectcomfort.com/portal'
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
def _setup_round(username, password, config, add_devices):
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
|
||||||
""" Sets up the honeywel thermostat. """
|
|
||||||
from evohomeclient import EvohomeClient
|
from evohomeclient import EvohomeClient
|
||||||
|
|
||||||
username = config.get(CONF_USERNAME)
|
|
||||||
password = config.get(CONF_PASSWORD)
|
|
||||||
try:
|
try:
|
||||||
away_temp = float(config.get(CONF_AWAY_TEMP, 16))
|
away_temp = float(config.get(CONF_AWAY_TEMP, 16))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
_LOGGER.error("value entered for item %s should convert to a number",
|
_LOGGER.error("value entered for item %s should convert to a number",
|
||||||
CONF_AWAY_TEMP)
|
CONF_AWAY_TEMP)
|
||||||
return False
|
return False
|
||||||
if username is None or password is None:
|
|
||||||
_LOGGER.error("Missing required configuration items %s or %s",
|
|
||||||
CONF_USERNAME, CONF_PASSWORD)
|
|
||||||
return False
|
|
||||||
|
|
||||||
evo_api = EvohomeClient(username, password)
|
evo_api = EvohomeClient(username, password)
|
||||||
|
|
||||||
@ -53,6 +52,24 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=unused-argument
|
||||||
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
""" Sets up the honeywel thermostat. """
|
||||||
|
username = config.get(CONF_USERNAME)
|
||||||
|
password = config.get(CONF_PASSWORD)
|
||||||
|
thermostat_id = config.get('id')
|
||||||
|
|
||||||
|
if username is None or password is None:
|
||||||
|
_LOGGER.error("Missing required configuration items %s or %s",
|
||||||
|
CONF_USERNAME, CONF_PASSWORD)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if thermostat_id:
|
||||||
|
add_devices([HoneywellUSThermostat(thermostat_id, username, password)])
|
||||||
|
else:
|
||||||
|
return _setup_round(username, password, config, add_devices)
|
||||||
|
|
||||||
|
|
||||||
class RoundThermostat(ThermostatDevice):
|
class RoundThermostat(ThermostatDevice):
|
||||||
""" Represents a Honeywell Round Connected thermostat. """
|
""" Represents a Honeywell Round Connected thermostat. """
|
||||||
|
|
||||||
@ -135,3 +152,117 @@ class RoundThermostat(ThermostatDevice):
|
|||||||
else:
|
else:
|
||||||
self._name = data['name']
|
self._name = data['name']
|
||||||
self._is_dhw = False
|
self._is_dhw = False
|
||||||
|
|
||||||
|
|
||||||
|
class HoneywellUSThermostat(ThermostatDevice):
|
||||||
|
""" Represents a Honeywell US Thermostat. """
|
||||||
|
|
||||||
|
def __init__(self, ident, username, password):
|
||||||
|
self._ident = ident
|
||||||
|
self._username = username
|
||||||
|
self._password = password
|
||||||
|
self._session = requests.Session()
|
||||||
|
# Maybe this should be configurable?
|
||||||
|
self._timeout = 30
|
||||||
|
# Yeah, really.
|
||||||
|
self._session.headers['X-Requested-With'] = 'XMLHttpRequest'
|
||||||
|
self._update()
|
||||||
|
|
||||||
|
def _login(self):
|
||||||
|
self._session.get(US_BASEURL, timeout=self._timeout)
|
||||||
|
params = {'UserName': self._username,
|
||||||
|
'Password': self._password,
|
||||||
|
'RememberMe': 'false',
|
||||||
|
'timeOffset': 480}
|
||||||
|
resp = self._session.post(US_BASEURL, params=params,
|
||||||
|
timeout=self._timeout)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
_LOGGER('Login failed for user %(user)s',
|
||||||
|
dict(user=self._username))
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _get_data(self):
|
||||||
|
if not self._login():
|
||||||
|
return
|
||||||
|
url = '%s/Device/CheckDataSession/%s' % (US_BASEURL, self._ident)
|
||||||
|
resp = self._session.get(url, timeout=self._timeout)
|
||||||
|
if resp.status_code < 300:
|
||||||
|
return resp.json()
|
||||||
|
else:
|
||||||
|
return {'error': resp.status_code}
|
||||||
|
|
||||||
|
def _set_data(self, data):
|
||||||
|
if not self._login():
|
||||||
|
return
|
||||||
|
url = '%s/Device/SubmitControlScreenChanges' % US_BASEURL
|
||||||
|
data['DeviceID'] = self._ident
|
||||||
|
resp = self._session.post(url, data=data, timeout=self._timeout)
|
||||||
|
if resp.status_code < 300:
|
||||||
|
return resp.json()
|
||||||
|
else:
|
||||||
|
return {'error': resp.status_code}
|
||||||
|
|
||||||
|
def _update(self):
|
||||||
|
self._data = self._get_data()['latestData']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_fan_on(self):
|
||||||
|
return self._data['fanData']['fanIsRunning']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return 'honeywell'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def unit_of_measurement(self):
|
||||||
|
unit = self._data['uiData']['DisplayUnits']
|
||||||
|
if unit == 'F':
|
||||||
|
return TEMP_FAHRENHEIT
|
||||||
|
else:
|
||||||
|
return TEMP_CELCIUS
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current_temperature(self):
|
||||||
|
self._update()
|
||||||
|
return self._data['uiData']['DispTemperature']
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target_temperature(self):
|
||||||
|
setpoint = US_SYSTEM_SWITCH_POSITIONS.get(
|
||||||
|
self._data['uiData']['SystemSwitchPosition'],
|
||||||
|
'Off')
|
||||||
|
return self._data['uiData']['%sSetpoint' % setpoint]
|
||||||
|
|
||||||
|
def set_temperature(self, temperature):
|
||||||
|
""" Set target temperature. """
|
||||||
|
data = {'SystemSwitch': None,
|
||||||
|
'HeatSetpoint': None,
|
||||||
|
'CoolSetpoint': None,
|
||||||
|
'HeatNextPeriod': None,
|
||||||
|
'CoolNextPeriod': None,
|
||||||
|
'StatusHeat': None,
|
||||||
|
'StatusCool': None,
|
||||||
|
'FanMode': None}
|
||||||
|
setpoint = US_SYSTEM_SWITCH_POSITIONS.get(
|
||||||
|
self._data['uiData']['SystemSwitchPosition'],
|
||||||
|
'Off')
|
||||||
|
data['%sSetpoint' % setpoint] = temperature
|
||||||
|
self._set_data(data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_state_attributes(self):
|
||||||
|
""" Return device specific state attributes. """
|
||||||
|
fanmodes = {0: "auto",
|
||||||
|
1: "on",
|
||||||
|
2: "circulate"}
|
||||||
|
return {"fan": (self._data['fanData']['fanIsRunning'] and
|
||||||
|
'running' or 'idle'),
|
||||||
|
"fanmode": fanmodes[self._data['fanData']['fanMode']]}
|
||||||
|
|
||||||
|
def turn_away_mode_on(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def turn_away_mode_off(self):
|
||||||
|
pass
|
||||||
|
Loading…
x
Reference in New Issue
Block a user