From 4ac9e9fc4c61cf94515a32c091126acbb52c0e62 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Thu, 8 Oct 2015 17:48:03 -0500 Subject: [PATCH 01/10] initial commit --- .../components/thermostat/radiotherm.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 homeassistant/components/thermostat/radiotherm.py diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py new file mode 100644 index 00000000000..28899fac56e --- /dev/null +++ b/homeassistant/components/thermostat/radiotherm.py @@ -0,0 +1,109 @@ +""" +homeassistant.components.thermostat.radiotherm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Adds support for Radio Thermostat wifi-enabled home thermostats +""" +import logging + +from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, + STATE_IDLE, STATE_HEAT) +from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) +from urllib.error import URLError + +#TODO: investigate why this fails +# REQUIREMENTS = ['radiotherm-1.2'] + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Sets up the Radio Thermostat. """ + logger = logging.getLogger(__name__) + + try: + import radiotherm + except ImportError: + logger.exception( + "Error while importing dependency radiotherm. " + "Did you maybe not install the radiotherm dependency?") + return + + host = config.get(CONF_HOST) + if host is None: + logger.error("host not defined in config.") + return + + try: + tstat = radiotherm.get_thermostat(host) + except URLError as err: + logger.Exception( + "Unable to connect to Radio Thermostat") + return + + add_devices([RadioThermostat(tstat)]) + + +class RadioThermostat(ThermostatDevice): + """ Represent a Radio Thermostat. """ + + def __init__(self, device, name=None): + self.device = device + self._name = name + + @property + def name(self): + """ Returns the name of the Radio Thermostat. """ + return 'radiothermostat' + #return self.device.name + + @property + def unit_of_measurement(self): + """ Unit of measurement this thermostat expresses itself in. """ + return TEMP_FAHRENHEIT + + @property + def device_state_attributes(self): + """ Returns device specific state attributes. """ + # Move these to Thermostat Device and make them global + return { + "humidity": None, + "target_humidity": None, + "fan": self.device.fmode['human'], + "mode": self.device.tmode['human'] + } + + + @property + def current_temperature(self): + """ Returns the current temperature. """ + return self.device.temp['raw'] + + @property + def operation(self): + """ Returns current operation. head, cool idle """ + if self.device.tmode['human'] == 'Cool': + return STATE_COOL + elif self.device.tmode['human'] == 'Heat': + return STATE_HEAT + else: + return STATE_IDLE + + @property + def target_temperature(self): + """ Returns the temperature we try to reach. """ + + if self.operation == STATE_COOL: + temp = self.device.t_cool['raw'] + elif self.operation == STATE_HEAT: + temp = self.device.t_heat['raw'] + + return round(temp, 1) + + + def set_temperate(self, temperature): + """ Set new target temperature """ + if self.operation == STATE_COOL: + self.device.t_cool = temperature + elif self.operation == STATE_HEAT: + self.device.t_heat + + + From e5d68d8a1e32e09d25c534a5eac8b5159a6301cb Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Fri, 9 Oct 2015 10:43:14 -0500 Subject: [PATCH 02/10] set name of device through hass config --- homeassistant/components/thermostat/radiotherm.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 28899fac56e..229fed220cd 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -27,6 +27,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): return host = config.get(CONF_HOST) + name = config.get(CONF_NAME) if host is None: logger.error("host not defined in config.") return @@ -38,7 +39,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Unable to connect to Radio Thermostat") return - add_devices([RadioThermostat(tstat)]) + add_devices([RadioThermostat(tstat, name)]) class RadioThermostat(ThermostatDevice): @@ -46,13 +47,13 @@ class RadioThermostat(ThermostatDevice): def __init__(self, device, name=None): self.device = device - self._name = name + if name: + self.set_name(name) @property def name(self): """ Returns the name of the Radio Thermostat. """ - return 'radiothermostat' - #return self.device.name + return self.device.name['raw'] @property def unit_of_measurement(self): @@ -98,12 +99,16 @@ class RadioThermostat(ThermostatDevice): return round(temp, 1) - def set_temperate(self, temperature): + def set_temperature(self, temperature): """ Set new target temperature """ if self.operation == STATE_COOL: self.device.t_cool = temperature elif self.operation == STATE_HEAT: self.device.t_heat + def set_name(self, name): + """ Set thermostat name """ + self.device.name = name + From fc1cf49fd3392624202d9a03696e43126c4ad55e Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Fri, 9 Oct 2015 10:49:54 -0500 Subject: [PATCH 03/10] added REQUIREMENTS for radiotherm python module --- homeassistant/components/thermostat/radiotherm.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 229fed220cd..8936ab15292 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -10,8 +10,7 @@ from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) from urllib.error import URLError -#TODO: investigate why this fails -# REQUIREMENTS = ['radiotherm-1.2'] +REQUIREMENTS = ['radiotherm'] def setup_platform(hass, config, add_devices, discovery_info=None): From 0cf909cce9ba47f34297e87ae800f49b7ea4e18a Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Fri, 9 Oct 2015 11:34:14 -0500 Subject: [PATCH 04/10] Correct ci failed tests --- homeassistant/components/thermostat/radiotherm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 8936ab15292..dcdb70840d4 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -33,8 +33,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: tstat = radiotherm.get_thermostat(host) - except URLError as err: - logger.Exception( + except URLError: + logger.exception( "Unable to connect to Radio Thermostat") return @@ -103,7 +103,7 @@ class RadioThermostat(ThermostatDevice): if self.operation == STATE_COOL: self.device.t_cool = temperature elif self.operation == STATE_HEAT: - self.device.t_heat + self.device.t_heat = temperature def set_name(self, name): """ Set thermostat name """ From a3d295d88588f1dfd08d1dd23d7feb16fd4762f3 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Fri, 9 Oct 2015 11:38:39 -0500 Subject: [PATCH 05/10] Correct formatting --- homeassistant/components/thermostat/radiotherm.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index dcdb70840d4..6aee1aa061d 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -70,7 +70,6 @@ class RadioThermostat(ThermostatDevice): "mode": self.device.tmode['human'] } - @property def current_temperature(self): """ Returns the current temperature. """ @@ -97,7 +96,6 @@ class RadioThermostat(ThermostatDevice): return round(temp, 1) - def set_temperature(self, temperature): """ Set new target temperature """ if self.operation == STATE_COOL: @@ -108,6 +106,3 @@ class RadioThermostat(ThermostatDevice): def set_name(self, name): """ Set thermostat name """ self.device.name = name - - - From 37278aab20e8a85357eac2ed3a2e38747704c309 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Sat, 10 Oct 2015 11:36:34 -0500 Subject: [PATCH 06/10] add set_time and begin discovery --- .../components/thermostat/radiotherm.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index 6aee1aa061d..b0ae38461b3 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -4,13 +4,14 @@ homeassistant.components.thermostat.radiotherm Adds support for Radio Thermostat wifi-enabled home thermostats """ import logging +import datetime +from urllib.error import URLError from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, STATE_IDLE, STATE_HEAT) from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) -from urllib.error import URLError -REQUIREMENTS = ['radiotherm'] +REQUIREMENTS = ['radiotherm==1.2'] def setup_platform(hass, config, add_devices, discovery_info=None): @@ -25,6 +26,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Did you maybe not install the radiotherm dependency?") return + hosts = [] + logger.info(discovery_info) + + host = config.get(CONF_HOST) name = config.get(CONF_NAME) if host is None: @@ -48,6 +53,7 @@ class RadioThermostat(ThermostatDevice): self.device = device if name: self.set_name(name) + self.set_time() @property def name(self): @@ -106,3 +112,9 @@ class RadioThermostat(ThermostatDevice): def set_name(self, name): """ Set thermostat name """ self.device.name = name + + def set_time(self): + """ Set device time """ + now = datetime.datetime.now() + self.device.time = {'day': now.weekday(), + 'hour': now.hour, 'minute': now.minute} From 84c72ebf634e0f89c381b46b44e9d050a053b9d0 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Sun, 11 Oct 2015 09:28:25 -0500 Subject: [PATCH 07/10] Add support for multiple thermostats (via hass-config) and auto-discovery via ratiotherm module --- .../components/thermostat/radiotherm.py | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index b0ae38461b3..e11f845b0b4 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -26,24 +26,31 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Did you maybe not install the radiotherm dependency?") return + # Detect hosts with hass discovery, config or radiotherm discovery hosts = [] - logger.info(discovery_info) + if discovery_info: + logger.info('hass radiotherm discovery', discovery_info) + elif CONF_HOST in config: + hosts = [config[CONF_HOST]] + else: + hosts.append(radiotherm.discover.discover_address()) - - host = config.get(CONF_HOST) name = config.get(CONF_NAME) - if host is None: - logger.error("host not defined in config.") + if hosts is None: + logger.error("no radiotherm thermostats detected") return - try: - tstat = radiotherm.get_thermostat(host) - except URLError: - logger.exception( - "Unable to connect to Radio Thermostat") - return + tstats = [] - add_devices([RadioThermostat(tstat, name)]) + for host in hosts: + try: + tstat = radiotherm.get_thermostat(host) + tstats.append(RadioThermostat(tstat)) + except (URLError, OSError): + logger.exception( + "Unable to connect to Radio Thermostat @{}".format(host)) + + add_devices(tstats) class RadioThermostat(ThermostatDevice): From 6c1c2430002db665a82266230a39a31e7b509c1a Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Sun, 11 Oct 2015 11:42:24 -0500 Subject: [PATCH 08/10] start away mode --- .../components/thermostat/radiotherm.py | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index e11f845b0b4..850bdcc2b83 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -9,7 +9,7 @@ from urllib.error import URLError from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, STATE_IDLE, STATE_HEAT) -from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) +from homeassistant.const import (CONF_HOST, TEMP_FAHRENHEIT) REQUIREMENTS = ['radiotherm==1.2'] @@ -29,13 +29,12 @@ def setup_platform(hass, config, add_devices, discovery_info=None): # Detect hosts with hass discovery, config or radiotherm discovery hosts = [] if discovery_info: - logger.info('hass radiotherm discovery', discovery_info) + logger.info('hass radiotherm discovery: %s', discovery_info) elif CONF_HOST in config: hosts = [config[CONF_HOST]] else: hosts.append(radiotherm.discover.discover_address()) - name = config.get(CONF_NAME) if hosts is None: logger.error("no radiotherm thermostats detected") return @@ -48,7 +47,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): tstats.append(RadioThermostat(tstat)) except (URLError, OSError): logger.exception( - "Unable to connect to Radio Thermostat @{}".format(host)) + "Unable to connect to Radio Thermostat: %s", host) add_devices(tstats) @@ -61,12 +60,19 @@ class RadioThermostat(ThermostatDevice): if name: self.set_name(name) self.set_time() + self._away = False + self._away_cool = 82 + self._away_heat = 70 @property def name(self): """ Returns the name of the Radio Thermostat. """ return self.device.name['raw'] + def set_name(self, name): + """ Set thermostat name """ + self.device.name = name + @property def unit_of_measurement(self): """ Unit of measurement this thermostat expresses itself in. """ @@ -116,9 +122,18 @@ class RadioThermostat(ThermostatDevice): elif self.operation == STATE_HEAT: self.device.t_heat = temperature - def set_name(self, name): - """ Set thermostat name """ - self.device.name = name + @property + def is_away_mode_on(self): + """ Returns away mode """ + return self._away + + def turn_away_mode_on(self): + """ Turns away mode on. """ + self._away = True + + def turn_away_mode_off(self): + """ Turns away mode off. """ + self._away = False def set_time(self): """ Set device time """ From b2e39884f90ccfbe965ddece44d04c4457916633 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Wed, 14 Oct 2015 11:02:07 -0500 Subject: [PATCH 09/10] Removed name and netdisco functions, implemented update method to caches values, radiotherm lib to coveragerc and requirements_all.txt --- .coveragerc | 1 + .../components/thermostat/radiotherm.py | 69 ++++++++----------- requirements_all.txt | 3 + 3 files changed, 33 insertions(+), 40 deletions(-) diff --git a/.coveragerc b/.coveragerc index 1ff145e2de3..e44a905a0ab 100644 --- a/.coveragerc +++ b/.coveragerc @@ -88,6 +88,7 @@ omit = homeassistant/components/switch/transmission.py homeassistant/components/switch/wemo.py homeassistant/components/thermostat/nest.py + homeassistant/components/thermostat/radiotherm.py [report] diff --git a/homeassistant/components/thermostat/radiotherm.py b/homeassistant/components/thermostat/radiotherm.py index e11f845b0b4..3acd3ef8986 100644 --- a/homeassistant/components/thermostat/radiotherm.py +++ b/homeassistant/components/thermostat/radiotherm.py @@ -9,33 +9,23 @@ from urllib.error import URLError from homeassistant.components.thermostat import (ThermostatDevice, STATE_COOL, STATE_IDLE, STATE_HEAT) -from homeassistant.const import (CONF_HOST, CONF_NAME, TEMP_FAHRENHEIT) +from homeassistant.const import (CONF_HOST, TEMP_FAHRENHEIT) REQUIREMENTS = ['radiotherm==1.2'] def setup_platform(hass, config, add_devices, discovery_info=None): """ Sets up the Radio Thermostat. """ + import radiotherm + logger = logging.getLogger(__name__) - try: - import radiotherm - except ImportError: - logger.exception( - "Error while importing dependency radiotherm. " - "Did you maybe not install the radiotherm dependency?") - return - - # Detect hosts with hass discovery, config or radiotherm discovery hosts = [] - if discovery_info: - logger.info('hass radiotherm discovery', discovery_info) - elif CONF_HOST in config: + if CONF_HOST in config: hosts = [config[CONF_HOST]] else: hosts.append(radiotherm.discover.discover_address()) - name = config.get(CONF_NAME) if hosts is None: logger.error("no radiotherm thermostats detected") return @@ -48,7 +38,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): tstats.append(RadioThermostat(tstat)) except (URLError, OSError): logger.exception( - "Unable to connect to Radio Thermostat @{}".format(host)) + "Unable to connect to Radio Thermostat: %s", host) add_devices(tstats) @@ -56,16 +46,19 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class RadioThermostat(ThermostatDevice): """ Represent a Radio Thermostat. """ - def __init__(self, device, name=None): + def __init__(self, device): self.device = device - if name: - self.set_name(name) self.set_time() + self._target_temperature = None + self._current_temperature = None + self._operation = STATE_IDLE + self._name = None + self.update() @property def name(self): """ Returns the name of the Radio Thermostat. """ - return self.device.name['raw'] + return self._name @property def unit_of_measurement(self): @@ -75,10 +68,7 @@ class RadioThermostat(ThermostatDevice): @property def device_state_attributes(self): """ Returns device specific state attributes. """ - # Move these to Thermostat Device and make them global return { - "humidity": None, - "target_humidity": None, "fan": self.device.fmode['human'], "mode": self.device.tmode['human'] } @@ -86,39 +76,38 @@ class RadioThermostat(ThermostatDevice): @property def current_temperature(self): """ Returns the current temperature. """ - return self.device.temp['raw'] + return round(self._current_temperature, 1) @property def operation(self): """ Returns current operation. head, cool idle """ - if self.device.tmode['human'] == 'Cool': - return STATE_COOL - elif self.device.tmode['human'] == 'Heat': - return STATE_HEAT - else: - return STATE_IDLE + return self._operation @property def target_temperature(self): """ Returns the temperature we try to reach. """ - if self.operation == STATE_COOL: - temp = self.device.t_cool['raw'] - elif self.operation == STATE_HEAT: - temp = self.device.t_heat['raw'] + return round(self._target_temperature, 1) - return round(temp, 1) + def update(self): + self._current_temperature = self.device.temp['raw'] + self._name = self.device.name['raw'] + if self.device.tmode['human'] == 'Cool': + self._target_temperature = self.device.t_cool['raw'] + self._operation = STATE_COOL + elif self.device.tmode['human'] == 'Heat': + self._target_temperature = self.device.t_heat['raw'] + self._operation = STATE_HEAT + else: + self._operation = STATE_IDLE def set_temperature(self, temperature): """ Set new target temperature """ - if self.operation == STATE_COOL: + if self._operation == STATE_COOL: self.device.t_cool = temperature - elif self.operation == STATE_HEAT: + elif self._operation == STATE_HEAT: self.device.t_heat = temperature - - def set_name(self, name): - """ Set thermostat name """ - self.device.name = name + self.device.hold = 1 def set_time(self): """ Set device time """ diff --git a/requirements_all.txt b/requirements_all.txt index 2b7074d91cd..668cedf65b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -137,3 +137,6 @@ SoCo==0.11.1 # PlexAPI (media_player.plex) https://github.com/adrienbrault/python-plexapi/archive/df2d0847e801d6d5cda920326d693cf75f304f1a.zip#python-plexapi==1.0.2 + +# Radio Thermostat (thermostat.radiotherm) +radiotherm=1.2 From b0bafa32b726c6a1a117d0945e628e8288a3fff1 Mon Sep 17 00:00:00 2001 From: Todd Ingarfield Date: Thu, 15 Oct 2015 11:44:19 -0500 Subject: [PATCH 10/10] fixed typo in requirements_all.txt --- requirements_all.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_all.txt b/requirements_all.txt index dae1f319a23..de1fa9e40ac 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -149,4 +149,4 @@ python-telegram-bot==2.8.7 py-cpuinfo==0.1.6 # Radio Thermostat (thermostat.radiotherm) -radiotherm=1.2 +radiotherm==1.2