From d3006b67a28ade33bcb9d5783d0af59672cfef7c Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sun, 16 Jul 2017 08:15:10 -0600 Subject: [PATCH 01/15] initial translation of zoneminder module into etherrain module --- homeassistant/components/etherrain.py | 102 ++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 homeassistant/components/etherrain.py diff --git a/homeassistant/components/etherrain.py b/homeassistant/components/etherrain.py new file mode 100644 index 00000000000000..1777120cb6f69d --- /dev/null +++ b/homeassistant/components/etherrain.py @@ -0,0 +1,102 @@ +""" +Support for Etherrain/8 + +""" +import logging +from urllib.parse import urljoin + +import requests +import voluptuous as vol + +from homeassistant.const import ( + CONF_PATH, CONF_HOST, CONF_SSL, CONF_PASSWORD, CONF_USERNAME) +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_SSL = False +DEFAULT_TIMEOUT = 10 +DOMAIN = 'etherrain' + +LOGIN_RETRIES = 2 + +ER = {} + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_HOST): cv.string, + vol.Required(CONF_USERNAME): cv.string, + vol.Required(CONF_PASSWORD): cv.string + }) +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Set up the Etherrain component.""" + global ER + ER = {} + + conf = config[DOMAIN] + schema = 'http' + + server_origin = '{}://{}'.format(schema, conf[CONF_HOST]) + username = conf.get(CONF_USERNAME, None) + password = conf.get(CONF_PASSWORD, None) + + ER['server_origin'] = server_origin + ER['username'] = username + ER['password'] = password + + hass.data[DOMAIN] = ER + + return login() + + +# pylint: disable=no-member +def login(): + """Login to the ZoneMinder API.""" + _LOGGER.info("Attempting to login to ZoneMinder") + + # ergetcfg.cgi?lu=admin\&lp=deadbeef + url = '{0}/ergetcfg.cgi?lu={1}&lp={2}'.format(ER['server_origin'],ER['username'],ER['password']) + req = requests.get(url, timeout=DEFAULT_TIMEOUT) + + if not req.ok: + _LOGGER.error("Connection error logging into EtherRain") + return False + + return True + + +def _er_request(method, api_url, data=None): + """Perform an EtherRain request.""" + for _ in range(LOGIN_RETRIES): + req = requests.request( + method, urljoin(ER['url'], api_url), data=data, + cookies=ER['cookies'], timeout=DEFAULT_TIMEOUT) + + if not req.ok: + login() + else: + break + + else: + _LOGGER.error("Unable to get API response from ZoneMinder") + + try: + return req.json() + except ValueError: + _LOGGER.exception('JSON decode exception caught while attempting to ' + 'decode "%s"', req.text) + + +# pylint: disable=no-member +def get_state(api_url): + """Get a state from the ZoneMinder API service.""" + return _er_request('get', api_url) + + +# pylint: disable=no-member +def change_state(api_url, valve_data): + """Update a state using the Zoneminder API.""" + return _zm_request('get', api_url, data=valve_data) From c5b453c31b0dda4768fcce757b365d717d9ba709 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sun, 30 Jul 2017 06:20:03 -0600 Subject: [PATCH 02/15] Switch code for Etherrain/8 component --- homeassistant/components/switch/etherrain.py | 84 ++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 homeassistant/components/switch/etherrain.py diff --git a/homeassistant/components/switch/etherrain.py b/homeassistant/components/switch/etherrain.py new file mode 100644 index 00000000000000..007961f8f700f4 --- /dev/null +++ b/homeassistant/components/switch/etherrain.py @@ -0,0 +1,84 @@ +""" +Support for Etherrain valves + +""" +import logging + +import voluptuous as vol + +from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) +from homeassistant.const import (CONF_COMMAND_ON, CONF_COMMAND_OFF) +import homeassistant.components.etherrain as er +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['etherrain'] + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_COMMAND_ON): cv.string, + vol.Required(CONF_COMMAND_OFF): cv.string, + vol.Required("valve_id"): cv.positive_int, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Etherrain irrigation platform.""" + on_state = config.get(CONF_COMMAND_ON) + off_state = config.get(CONF_COMMAND_OFF) + valve_id = config.get("valve_id") + valve_name = config.get("name") + _LOGGER.info("Setting up etherrain switch {0}".format(valve_id)) + + add_devices([ERValveSwitches(valve_id,valve_name, on_state,off_state)]) + + +class ERValveSwitches(SwitchDevice): + """Representation of an Etherrain valve.""" + + icon = 'mdi:record-rec' + + def __init__(self, valve_id, valve_name, on_state, off_state): + """Initialize the switch.""" + self._valve_id = valve_id + self._valve_name = valve_name + self._duration = 0 + self._on_state = on_state + self._off_state = off_state + self._state = None + + @property + def name(self): + return self._valve_name + + def update(self): + state = er.get_state(self._valve_id) + + if state == 1: + self._state = True + else: + self._state = False + _LOGGER.info("update etherrain switch {0} - {1}".format(self._valve_id, self._state)) + + @property + def is_on(self): + _LOGGER.info("is_on: etherrain switch {0} - {1}".format(self._valve_id, self._state)) + return self._state + + def turn_on(self): + valve={} + valve["duration"] = 60 + valve["valve"] = self._valve_id + valve["command"] = er.WATER_ON + _LOGGER.info("turn on etherrain switch {0}".format(self._valve_id)) + self._state = True + er.change_state(valve) + + def turn_off(self): + valve={} + valve["duration"] = 0 + valve["valve"] = 0 + valve["command"] = er.WATER_OFF + self._state = False + _LOGGER.info("turn off etherrain switch {0}".format(self._valve_id)) + er.change_state(valve) From c838277ceafdb89074afe92a78db34c6d2ebc8c4 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sun, 30 Jul 2017 06:37:16 -0600 Subject: [PATCH 03/15] Clean up some extraneous logging --- homeassistant/components/etherrain.py | 103 +++++++++++++++---- homeassistant/components/switch/etherrain.py | 8 +- 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/etherrain.py b/homeassistant/components/etherrain.py index 1777120cb6f69d..9ee63122dbd4cd 100644 --- a/homeassistant/components/etherrain.py +++ b/homeassistant/components/etherrain.py @@ -17,6 +17,9 @@ DEFAULT_SSL = False DEFAULT_TIMEOUT = 10 DOMAIN = 'etherrain' +STATE=1 +WATER_ON=2 +WATER_OFF=3 LOGIN_RETRIES = 2 @@ -54,8 +57,8 @@ def setup(hass, config): # pylint: disable=no-member def login(): - """Login to the ZoneMinder API.""" - _LOGGER.info("Attempting to login to ZoneMinder") + """Login to the EtherRain API.""" + _LOGGER.info("Attempting to login to EtherRain") # ergetcfg.cgi?lu=admin\&lp=deadbeef url = '{0}/ergetcfg.cgi?lu={1}&lp={2}'.format(ER['server_origin'],ER['username'],ER['password']) @@ -68,35 +71,95 @@ def login(): return True -def _er_request(method, api_url, data=None): +# http://er_addr/result.cgi?xi=0:1:0:0:0:0:0:0:0 +def _er_request(data=None): """Perform an EtherRain request.""" + valve = 0 + duration=0 + cmd = 0 + if data is not 'None': + cmd=data["command"] + valve=data["valve"] + duration=data["duration"] + if cmd == STATE: + #_LOGGER.info("Get state".format(valve,duration)) + url = '{0}/result.cgi?xs'.format(ER['server_origin']) + duration = 0 + if cmd == WATER_OFF: + #_LOGGER.info("Water off".format(valve,duration)) + url = '{0}/result.cgi?xr'.format(ER['server_origin']) + if cmd == WATER_ON: + _LOGGER.info("Set {0} to {1} minutes".format(valve,duration)) + valves = [ "0", "0", "0", "0", "0", "0", "0", "0", "0" ] + valves[valve] = duration + url = '{0}/result.cgi?xi=0:{1}:{2}:{3}:{4}:{5}:{6}:{7}:{8}'.format(ER['server_origin'], valves[1], valves[2], valves[3], valves[4], valves[5], valves[6], valves[7], valves[8] ) + for _ in range(LOGIN_RETRIES): - req = requests.request( - method, urljoin(ER['url'], api_url), data=data, - cookies=ER['cookies'], timeout=DEFAULT_TIMEOUT) + #_LOGGER.info("url is {0}".format(url)) + req = requests.get(url) + #_LOGGER.info("Returned: {0}".format(req.status_code)) if not req.ok: login() else: break - else: - _LOGGER.error("Unable to get API response from ZoneMinder") - - try: - return req.json() - except ValueError: - _LOGGER.exception('JSON decode exception caught while attempting to ' - 'decode "%s"', req.text) + _LOGGER.error("Unable to get API response from EtherRain") + return req # pylint: disable=no-member -def get_state(api_url): - """Get a state from the ZoneMinder API service.""" - return _er_request('get', api_url) +# retrieve current status +# http:///result.cgi?xs +# +# +# EtherRain Device Status
+# un:EtherRain 8 +# ma: 01.00.44.03.0A.01
+# ac:
+# os: RD
+# cs: OK
+# rz: UK
+# ri: 0
+# rn: 0
+# +# +def get_state(valve): + """Get a state from the EtherRain API service.""" + data={} + data["valve"] = valve + data["duration"] = 0 + data["command"] = STATE + state = _er_request(data) + # _LOGGER.info("got {0} from etherrain".format(state.text)) + status = {} + for b_line in state.iter_lines(): + line = b_line.decode('utf8').strip() + # _LOGGER.info("iterating: {0}".format(line)) + if ":" in line: + attr,value=line.split(":") + value = value.replace("
","").strip() + attr = attr.strip() + if attr in [ 'ac', 'os', 'cs', 'rz', 'ri', 'rn' ] : + status[attr]=value + + # ri contains the last valve to run. os is the current state of that valve. (ready or busy) + # (XXX: The valve number returned is 0-7 but the watering command takes 1-8) + if 'os' in status and status['os'] == 'WT': + # _LOGGER.info("valve is {0} and is waiting".format(valve, int(status['ri']))) + return 1 + if 'ri' in status and int(status['ri']) == valve-1: + if 'os' in status and status['os'] == 'RD': + # _LOGGER.info("last valve is {0} and is ready".format(valve, int(status['ri']))) + return 0 + if 'os' in status and status['os'] == 'BZ': + # _LOGGER.info("valve is {0} and is busy".format(valve, int(status['ri']))) + return 1 + else: + return 0 # pylint: disable=no-member -def change_state(api_url, valve_data): - """Update a state using the Zoneminder API.""" - return _zm_request('get', api_url, data=valve_data) +def change_state(valve_data): + _LOGGER.info("Change State: {0}".format(valve_data)) + return _er_request(data=valve_data) diff --git a/homeassistant/components/switch/etherrain.py b/homeassistant/components/switch/etherrain.py index 007961f8f700f4..ca66bae0b1ad2f 100644 --- a/homeassistant/components/switch/etherrain.py +++ b/homeassistant/components/switch/etherrain.py @@ -58,11 +58,11 @@ def update(self): self._state = True else: self._state = False - _LOGGER.info("update etherrain switch {0} - {1}".format(self._valve_id, self._state)) + # _LOGGER.info("update etherrain switch {0} - {1}".format(self._valve_id, self._state)) @property def is_on(self): - _LOGGER.info("is_on: etherrain switch {0} - {1}".format(self._valve_id, self._state)) + # _LOGGER.info("is_on: etherrain switch {0} - {1}".format(self._valve_id, self._state)) return self._state def turn_on(self): @@ -70,7 +70,7 @@ def turn_on(self): valve["duration"] = 60 valve["valve"] = self._valve_id valve["command"] = er.WATER_ON - _LOGGER.info("turn on etherrain switch {0}".format(self._valve_id)) + # _LOGGER.info("turn on etherrain switch {0}".format(self._valve_id)) self._state = True er.change_state(valve) @@ -80,5 +80,5 @@ def turn_off(self): valve["valve"] = 0 valve["command"] = er.WATER_OFF self._state = False - _LOGGER.info("turn off etherrain switch {0}".format(self._valve_id)) + # _LOGGER.info("turn off etherrain switch {0}".format(self._valve_id)) er.change_state(valve) From 866f0463f43e1cfa59ac1042b712c4584dc89637 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sat, 5 Aug 2017 05:53:39 -0600 Subject: [PATCH 04/15] port over a zoneminder sensor to etherrain --- homeassistant/components/sensor/etherrain.py | 66 ++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 homeassistant/components/sensor/etherrain.py diff --git a/homeassistant/components/sensor/etherrain.py b/homeassistant/components/sensor/etherrain.py new file mode 100644 index 00000000000000..bf7cc473d43a30 --- /dev/null +++ b/homeassistant/components/sensor/etherrain.py @@ -0,0 +1,66 @@ +""" +Support for Etherrain Sensors. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.etherrain/ +""" +import logging + +import voluptuous as vol + +from homeassistant.components.sensor import PLATFORM_SCHEMA +from homeassistant.const import STATE_UNKNOWN +from homeassistant.helpers.entity import Entity +import homeassistant.components.etherrain as er +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['etherrain'] + +CONF_INCLUDE_ARCHIVED = "include_archived" + +DEFAULT_INCLUDE_ARCHIVED = False + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Optional(CONF_INCLUDE_ARCHIVED, default=DEFAULT_INCLUDE_ARCHIVED): + cv.boolean, +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + include_archived = config.get(CONF_INCLUDE_ARCHIVED) + valve_id = config.get("valve_id") + valve_name = config.get("name") + + add_devices([ERValveSensors(valve_id,valve_name)]) + + +class ERValveSensors(Entity): + + def __init__(self, valve_id, valve_name): + self._valve_id = valve_id + self._valve_name = valve_name + self._state = None + + @property + def name(self): + return self._valve_name + + @property + def state(self): + return self._state + + def update(self): + state = er.get_state(self._valve_id) + + if state == 1: + self._state = True + else: + self._state = False + # _LOGGER.info("update etherrain switch {0} - {1}".format(self._valve_id, self._state)) + + @property + def is_on(self): + # _LOGGER.info("is_on: etherrain switch {0} - {1}".format(self._valve_id, self._state)) + return self._state From 54380599bb8e200bea9ab19e33fe426dffd97414 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sat, 5 Aug 2017 05:53:58 -0600 Subject: [PATCH 05/15] remove redundant comment --- homeassistant/components/switch/etherrain.py | 1 - 1 file changed, 1 deletion(-) diff --git a/homeassistant/components/switch/etherrain.py b/homeassistant/components/switch/etherrain.py index ca66bae0b1ad2f..7bef7c02dbbced 100644 --- a/homeassistant/components/switch/etherrain.py +++ b/homeassistant/components/switch/etherrain.py @@ -39,7 +39,6 @@ class ERValveSwitches(SwitchDevice): icon = 'mdi:record-rec' def __init__(self, valve_id, valve_name, on_state, off_state): - """Initialize the switch.""" self._valve_id = valve_id self._valve_name = valve_name self._duration = 0 From 60a5e36a0ceccc2d691d667336795f13a5e3b274 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sat, 5 Aug 2017 06:51:25 -0600 Subject: [PATCH 06/15] pass flake8 --- homeassistant/components/etherrain.py | 70 +++++++++++++++------------ 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/etherrain.py b/homeassistant/components/etherrain.py index 9ee63122dbd4cd..6302758c6e08d1 100644 --- a/homeassistant/components/etherrain.py +++ b/homeassistant/components/etherrain.py @@ -1,25 +1,26 @@ -""" -Support for Etherrain/8 +""". + +Support for Etherrain/8. """ import logging -from urllib.parse import urljoin import requests import voluptuous as vol from homeassistant.const import ( - CONF_PATH, CONF_HOST, CONF_SSL, CONF_PASSWORD, CONF_USERNAME) + CONF_HOST, CONF_PASSWORD, CONF_USERNAME) import homeassistant.helpers.config_validation as cv + _LOGGER = logging.getLogger(__name__) DEFAULT_SSL = False DEFAULT_TIMEOUT = 10 DOMAIN = 'etherrain' -STATE=1 -WATER_ON=2 -WATER_OFF=3 +STATE = 1 +WATER_ON = 2 +WATER_OFF = 3 LOGIN_RETRIES = 2 @@ -61,7 +62,8 @@ def login(): _LOGGER.info("Attempting to login to EtherRain") # ergetcfg.cgi?lu=admin\&lp=deadbeef - url = '{0}/ergetcfg.cgi?lu={1}&lp={2}'.format(ER['server_origin'],ER['username'],ER['password']) + url = '{0}/ergetcfg.cgi?lu={1}&lp={2}'.format( + ER['server_origin'], ER['username'], ER['password']) req = requests.get(url, timeout=DEFAULT_TIMEOUT) if not req.ok: @@ -75,29 +77,31 @@ def login(): def _er_request(data=None): """Perform an EtherRain request.""" valve = 0 - duration=0 + duration = 0 cmd = 0 if data is not 'None': - cmd=data["command"] - valve=data["valve"] - duration=data["duration"] + cmd = data["command"] + valve = data["valve"] + duration = data["duration"] if cmd == STATE: - #_LOGGER.info("Get state".format(valve,duration)) + # _LOGGER.info("Get state".format(valve,duration)) url = '{0}/result.cgi?xs'.format(ER['server_origin']) duration = 0 if cmd == WATER_OFF: - #_LOGGER.info("Water off".format(valve,duration)) + # _LOGGER.info("Water off".format(valve,duration)) url = '{0}/result.cgi?xr'.format(ER['server_origin']) if cmd == WATER_ON: - _LOGGER.info("Set {0} to {1} minutes".format(valve,duration)) - valves = [ "0", "0", "0", "0", "0", "0", "0", "0", "0" ] + _LOGGER.info("Set {0} to {1} minutes".format(valve, duration)) + valves = ["0", "0", "0", "0", "0", "0", "0", "0", "0"] valves[valve] = duration - url = '{0}/result.cgi?xi=0:{1}:{2}:{3}:{4}:{5}:{6}:{7}:{8}'.format(ER['server_origin'], valves[1], valves[2], valves[3], valves[4], valves[5], valves[6], valves[7], valves[8] ) + url = '{0}/result.cgi?xi=0:{1}:{2}:{3}:{4}:{5}:{6}:{7}:{8}'.format( + ER['server_origin'], valves[1], valves[2], valves[3], valves[4], + valves[5], valves[6], valves[7], valves[8]) for _ in range(LOGIN_RETRIES): - #_LOGGER.info("url is {0}".format(url)) + # _LOGGER.info("url is {0}".format(url)) req = requests.get(url) - #_LOGGER.info("Returned: {0}".format(req.status_code)) + # _LOGGER.info("Returned: {0}".format(req.status_code)) if not req.ok: login() @@ -108,7 +112,7 @@ def _er_request(data=None): return req -# pylint: disable=no-member + # retrieve current status # http:///result.cgi?xs # @@ -123,10 +127,9 @@ def _er_request(data=None): # ri: 0
# rn: 0
# -# def get_state(valve): - """Get a state from the EtherRain API service.""" - data={} + """Get the current state of a valve.""" + data = {} data["valve"] = valve data["duration"] = 0 data["command"] = STATE @@ -137,23 +140,25 @@ def get_state(valve): line = b_line.decode('utf8').strip() # _LOGGER.info("iterating: {0}".format(line)) if ":" in line: - attr,value=line.split(":") - value = value.replace("
","").strip() + attr, value = line.split(":") + value = value.replace("
", "").strip() attr = attr.strip() - if attr in [ 'ac', 'os', 'cs', 'rz', 'ri', 'rn' ] : - status[attr]=value + if attr in ['ac', 'os', 'cs', 'rz', 'ri', 'rn']: + status[attr] = value - # ri contains the last valve to run. os is the current state of that valve. (ready or busy) - # (XXX: The valve number returned is 0-7 but the watering command takes 1-8) + # ri contains the last valve to run. os is the current state of that + # valve. (ready or busy) + # (XXX: The valve number returned is 0-7 but the watering command + # takes 1-8) if 'os' in status and status['os'] == 'WT': - # _LOGGER.info("valve is {0} and is waiting".format(valve, int(status['ri']))) + # _LOGGER.info("valve={0} and waiting".format(valve, status['ri'])) return 1 if 'ri' in status and int(status['ri']) == valve-1: if 'os' in status and status['os'] == 'RD': - # _LOGGER.info("last valve is {0} and is ready".format(valve, int(status['ri']))) + # _LOGGER.info("valve={0} and ready".format(valve, status['ri'])) return 0 if 'os' in status and status['os'] == 'BZ': - # _LOGGER.info("valve is {0} and is busy".format(valve, int(status['ri']))) + # _LOGGER.info("valve={0} and busy".format(valve, status['ri'])) return 1 else: return 0 @@ -161,5 +166,6 @@ def get_state(valve): # pylint: disable=no-member def change_state(valve_data): + """Change the state of a valve.""" _LOGGER.info("Change State: {0}".format(valve_data)) return _er_request(data=valve_data) From 78b8ebd935d79f4e79e3717cb720271b622b36a7 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sat, 5 Aug 2017 07:04:45 -0600 Subject: [PATCH 07/15] pass flake8 --- homeassistant/components/sensor/etherrain.py | 36 +++++++++----------- homeassistant/components/switch/etherrain.py | 28 ++++++++++----- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/homeassistant/components/sensor/etherrain.py b/homeassistant/components/sensor/etherrain.py index bf7cc473d43a30..24253bb8b7cef3 100644 --- a/homeassistant/components/sensor/etherrain.py +++ b/homeassistant/components/sensor/etherrain.py @@ -1,66 +1,62 @@ -""" +""". + Support for Etherrain Sensors. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.etherrain/ + """ import logging -import voluptuous as vol - -from homeassistant.components.sensor import PLATFORM_SCHEMA -from homeassistant.const import STATE_UNKNOWN from homeassistant.helpers.entity import Entity import homeassistant.components.etherrain as er -import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['etherrain'] -CONF_INCLUDE_ARCHIVED = "include_archived" - -DEFAULT_INCLUDE_ARCHIVED = False - -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Optional(CONF_INCLUDE_ARCHIVED, default=DEFAULT_INCLUDE_ARCHIVED): - cv.boolean, -}) - def setup_platform(hass, config, add_devices, discovery_info=None): - include_archived = config.get(CONF_INCLUDE_ARCHIVED) + """Setup sensor platform.""" valve_id = config.get("valve_id") valve_name = config.get("name") - add_devices([ERValveSensors(valve_id,valve_name)]) + add_devices([ERValveSensors(valve_id, valve_name)]) class ERValveSensors(Entity): + """Representation of an Etherrain valve.""" def __init__(self, valve_id, valve_name): + """Init valve sensors.""" self._valve_id = valve_id self._valve_name = valve_name self._state = None @property def name(self): + """Return valve name.""" return self._valve_name @property def state(self): + """Return valve state.""" return self._state def update(self): + """Update valve state.""" state = er.get_state(self._valve_id) - + if state == 1: self._state = True else: self._state = False - # _LOGGER.info("update etherrain switch {0} - {1}".format(self._valve_id, self._state)) + # _LOGGER.info("update etherrain switch {0} - {1}".format( + # self._valve_id, self._state)) @property def is_on(self): - # _LOGGER.info("is_on: etherrain switch {0} - {1}".format(self._valve_id, self._state)) + """Return valve state.""" + # _LOGGER.info("is_on: etherrain switch {0} - {1}".format( + # self._valve_id, self._state)) return self._state diff --git a/homeassistant/components/switch/etherrain.py b/homeassistant/components/switch/etherrain.py index 7bef7c02dbbced..57847b0360562d 100644 --- a/homeassistant/components/switch/etherrain.py +++ b/homeassistant/components/switch/etherrain.py @@ -1,5 +1,6 @@ -""" -Support for Etherrain valves +""". + +Support for Etherrain valves. """ import logging @@ -11,6 +12,7 @@ import homeassistant.components.etherrain as er import homeassistant.helpers.config_validation as cv + _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['etherrain'] @@ -30,7 +32,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): valve_name = config.get("name") _LOGGER.info("Setting up etherrain switch {0}".format(valve_id)) - add_devices([ERValveSwitches(valve_id,valve_name, on_state,off_state)]) + add_devices([ERValveSwitches(valve_id, valve_name, on_state, off_state)]) class ERValveSwitches(SwitchDevice): @@ -39,6 +41,7 @@ class ERValveSwitches(SwitchDevice): icon = 'mdi:record-rec' def __init__(self, valve_id, valve_name, on_state, off_state): + """Initialize ERValveSwitches.""" self._valve_id = valve_id self._valve_name = valve_name self._duration = 0 @@ -48,33 +51,40 @@ def __init__(self, valve_id, valve_name, on_state, off_state): @property def name(self): + """Get valve name.""" return self._valve_name def update(self): + """Update valve state.""" state = er.get_state(self._valve_id) - + if state == 1: self._state = True else: self._state = False - # _LOGGER.info("update etherrain switch {0} - {1}".format(self._valve_id, self._state)) + # _LOGGER.info("update etherrain switch {0} - {1}".format( + # self._valve_id, self._state)) @property def is_on(self): - # _LOGGER.info("is_on: etherrain switch {0} - {1}".format(self._valve_id, self._state)) + """Return valve state.""" + # _LOGGER.info("is_on: etherrain switch {0} - {1}".format( + # self._valve_id, self._state)) return self._state def turn_on(self): - valve={} + """Turn a valve on.""" + valve = {} valve["duration"] = 60 valve["valve"] = self._valve_id valve["command"] = er.WATER_ON # _LOGGER.info("turn on etherrain switch {0}".format(self._valve_id)) - self._state = True + self._state = True er.change_state(valve) def turn_off(self): - valve={} + """Turn a valve off.""" + valve = {} valve["duration"] = 0 valve["valve"] = 0 valve["command"] = er.WATER_OFF From 3839efc18647537cdeff57f4ea73b1aa3ef51194 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sat, 5 Aug 2017 07:09:13 -0600 Subject: [PATCH 08/15] pass flake8/pylint/pydoctstyle --- homeassistant/components/sensor/etherrain.py | 5 +---- homeassistant/components/switch/etherrain.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/sensor/etherrain.py b/homeassistant/components/sensor/etherrain.py index 24253bb8b7cef3..ecf444a779c0ff 100644 --- a/homeassistant/components/sensor/etherrain.py +++ b/homeassistant/components/sensor/etherrain.py @@ -47,10 +47,7 @@ def update(self): """Update valve state.""" state = er.get_state(self._valve_id) - if state == 1: - self._state = True - else: - self._state = False + self._state = state # _LOGGER.info("update etherrain switch {0} - {1}".format( # self._valve_id, self._state)) diff --git a/homeassistant/components/switch/etherrain.py b/homeassistant/components/switch/etherrain.py index 57847b0360562d..e7f4c19dfe52a4 100644 --- a/homeassistant/components/switch/etherrain.py +++ b/homeassistant/components/switch/etherrain.py @@ -58,10 +58,7 @@ def update(self): """Update valve state.""" state = er.get_state(self._valve_id) - if state == 1: - self._state = True - else: - self._state = False + self._state = state # _LOGGER.info("update etherrain switch {0} - {1}".format( # self._valve_id, self._state)) From ff7d1335475dea2c6465a9346b7f55fed9e20db0 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sat, 5 Aug 2017 07:21:45 -0600 Subject: [PATCH 09/15] Always log in for every request. The etherrain/8 will only take commands from the last IP that logged in to it. So if a different host on the network is also issuing commands to the ER/8, then our commands will fail. There is obviously a bit of a race condition here. This at least somewhat mitigates the problem. --- homeassistant/components/etherrain.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/etherrain.py b/homeassistant/components/etherrain.py index 6302758c6e08d1..a1d132c63768e3 100644 --- a/homeassistant/components/etherrain.py +++ b/homeassistant/components/etherrain.py @@ -59,7 +59,7 @@ def setup(hass, config): # pylint: disable=no-member def login(): """Login to the EtherRain API.""" - _LOGGER.info("Attempting to login to EtherRain") + # _LOGGER.info("Attempting to login to EtherRain") # ergetcfg.cgi?lu=admin\&lp=deadbeef url = '{0}/ergetcfg.cgi?lu={1}&lp={2}'.format( @@ -98,16 +98,17 @@ def _er_request(data=None): ER['server_origin'], valves[1], valves[2], valves[3], valves[4], valves[5], valves[6], valves[7], valves[8]) - for _ in range(LOGIN_RETRIES): - # _LOGGER.info("url is {0}".format(url)) - req = requests.get(url) - # _LOGGER.info("Returned: {0}".format(req.status_code)) + # Always log in. The etherrain/8 will only take commands from the last IP + # that logged in to it. So if a different host on the network is also + # issuing commands to the ER/8, then our commands will fail. There is + # obviously a bit of a race condition here. This at least somewhat + # mitigates the problem. + login() + # _LOGGER.info("url is {0}".format(url)) + req = requests.get(url) + # _LOGGER.info("Returned: {0}".format(req.status_code)) - if not req.ok: - login() - else: - break - else: + if not req.ok: _LOGGER.error("Unable to get API response from EtherRain") return req From 79b692cee00ba9e7977ff09d861a8bed40294a2c Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sat, 5 Aug 2017 07:25:13 -0600 Subject: [PATCH 10/15] etherrain has no tests --- .coveragerc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.coveragerc b/.coveragerc index 2d1bff462b963d..85eb1e95b62dea 100644 --- a/.coveragerc +++ b/.coveragerc @@ -53,6 +53,9 @@ omit = homeassistant/components/eight_sleep.py homeassistant/components/*/eight_sleep.py + homeassistant/components/etherrain.py + homeassistant/components/*/etherrain.py + homeassistant/components/ecobee.py homeassistant/components/*/ecobee.py From 86494a18263bb5e01d5ae8721d962f3a8b11b8ca Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sat, 5 Aug 2017 07:42:21 -0600 Subject: [PATCH 11/15] remove command_on/command_off. Change icon --- homeassistant/components/switch/etherrain.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/switch/etherrain.py b/homeassistant/components/switch/etherrain.py index e7f4c19dfe52a4..ef63dc230d7da8 100644 --- a/homeassistant/components/switch/etherrain.py +++ b/homeassistant/components/switch/etherrain.py @@ -8,7 +8,6 @@ import voluptuous as vol from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) -from homeassistant.const import (CONF_COMMAND_ON, CONF_COMMAND_OFF) import homeassistant.components.etherrain as er import homeassistant.helpers.config_validation as cv @@ -18,35 +17,29 @@ DEPENDENCIES = ['etherrain'] PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_COMMAND_ON): cv.string, - vol.Required(CONF_COMMAND_OFF): cv.string, vol.Required("valve_id"): cv.positive_int, }) def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Etherrain irrigation platform.""" - on_state = config.get(CONF_COMMAND_ON) - off_state = config.get(CONF_COMMAND_OFF) valve_id = config.get("valve_id") valve_name = config.get("name") _LOGGER.info("Setting up etherrain switch {0}".format(valve_id)) - add_devices([ERValveSwitches(valve_id, valve_name, on_state, off_state)]) + add_devices([ERValveSwitches(valve_id, valve_name)]) class ERValveSwitches(SwitchDevice): """Representation of an Etherrain valve.""" - icon = 'mdi:record-rec' - - def __init__(self, valve_id, valve_name, on_state, off_state): + def __init__(self, valve_id, valve_name): """Initialize ERValveSwitches.""" self._valve_id = valve_id self._valve_name = valve_name self._duration = 0 - self._on_state = on_state - self._off_state = off_state + self._on_state = 1 + self._off_state = 0 self._state = None @property From 16ffe8f5e1a3a019f355eed6ff7464d94b5f4d31 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Tue, 8 Aug 2017 07:06:59 -0600 Subject: [PATCH 12/15] allow duration to be overridden in config --- homeassistant/components/sensor/etherrain.py | 4 +--- homeassistant/components/switch/etherrain.py | 15 ++++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/sensor/etherrain.py b/homeassistant/components/sensor/etherrain.py index ecf444a779c0ff..e6d7231b1e4d74 100644 --- a/homeassistant/components/sensor/etherrain.py +++ b/homeassistant/components/sensor/etherrain.py @@ -45,9 +45,7 @@ def state(self): def update(self): """Update valve state.""" - state = er.get_state(self._valve_id) - - self._state = state + self._state = er.get_state(self._valve_id) # _LOGGER.info("update etherrain switch {0} - {1}".format( # self._valve_id, self._state)) diff --git a/homeassistant/components/switch/etherrain.py b/homeassistant/components/switch/etherrain.py index ef63dc230d7da8..8ad172b6fdb015 100644 --- a/homeassistant/components/switch/etherrain.py +++ b/homeassistant/components/switch/etherrain.py @@ -25,19 +25,22 @@ def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the Etherrain irrigation platform.""" valve_id = config.get("valve_id") valve_name = config.get("name") - _LOGGER.info("Setting up etherrain switch {0}".format(valve_id)) + duration = config.get("duration") - add_devices([ERValveSwitches(valve_id, valve_name)]) + add_devices([ERValveSwitches(valve_id, valve_name, duration)]) class ERValveSwitches(SwitchDevice): """Representation of an Etherrain valve.""" - def __init__(self, valve_id, valve_name): + def __init__(self, valve_id, valve_name, duration): """Initialize ERValveSwitches.""" self._valve_id = valve_id self._valve_name = valve_name - self._duration = 0 + if duration is not None: + self._duration = duration + else: + self._duration = 60 self._on_state = 1 self._off_state = 0 self._state = None @@ -65,7 +68,7 @@ def is_on(self): def turn_on(self): """Turn a valve on.""" valve = {} - valve["duration"] = 60 + valve["duration"] = self._duration valve["valve"] = self._valve_id valve["command"] = er.WATER_ON # _LOGGER.info("turn on etherrain switch {0}".format(self._valve_id)) @@ -74,6 +77,8 @@ def turn_on(self): def turn_off(self): """Turn a valve off.""" + # We should first check the state and if it's "BZ" and the valve_id matches, + # then turn it off. For now, just turn it off regardless. valve = {} valve["duration"] = 0 valve["valve"] = 0 From eb4bbe5817fa036d48f6dabd7a5d3ecb63500def Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Wed, 9 Aug 2017 08:00:52 -0600 Subject: [PATCH 13/15] line too long fix... --- homeassistant/components/switch/etherrain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switch/etherrain.py b/homeassistant/components/switch/etherrain.py index 8ad172b6fdb015..333921f1304547 100644 --- a/homeassistant/components/switch/etherrain.py +++ b/homeassistant/components/switch/etherrain.py @@ -77,8 +77,8 @@ def turn_on(self): def turn_off(self): """Turn a valve off.""" - # We should first check the state and if it's "BZ" and the valve_id matches, - # then turn it off. For now, just turn it off regardless. + # We should first check the state and if it's "BZ" and the valve_id + # matches, then turn it off. For now, just turn it off regardless. valve = {} valve["duration"] = 0 valve["valve"] = 0 From e26e496f3714cfd341859422afa6ea3641105611 Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Thu, 10 Aug 2017 05:34:39 -0600 Subject: [PATCH 14/15] EtherRain sensor is redundant. --- homeassistant/components/sensor/etherrain.py | 57 -------------------- 1 file changed, 57 deletions(-) delete mode 100644 homeassistant/components/sensor/etherrain.py diff --git a/homeassistant/components/sensor/etherrain.py b/homeassistant/components/sensor/etherrain.py deleted file mode 100644 index e6d7231b1e4d74..00000000000000 --- a/homeassistant/components/sensor/etherrain.py +++ /dev/null @@ -1,57 +0,0 @@ -""". - -Support for Etherrain Sensors. - -For more details about this platform, please refer to the documentation at -https://home-assistant.io/components/sensor.etherrain/ - -""" -import logging - -from homeassistant.helpers.entity import Entity -import homeassistant.components.etherrain as er - -_LOGGER = logging.getLogger(__name__) - -DEPENDENCIES = ['etherrain'] - - -def setup_platform(hass, config, add_devices, discovery_info=None): - """Setup sensor platform.""" - valve_id = config.get("valve_id") - valve_name = config.get("name") - - add_devices([ERValveSensors(valve_id, valve_name)]) - - -class ERValveSensors(Entity): - """Representation of an Etherrain valve.""" - - def __init__(self, valve_id, valve_name): - """Init valve sensors.""" - self._valve_id = valve_id - self._valve_name = valve_name - self._state = None - - @property - def name(self): - """Return valve name.""" - return self._valve_name - - @property - def state(self): - """Return valve state.""" - return self._state - - def update(self): - """Update valve state.""" - self._state = er.get_state(self._valve_id) - # _LOGGER.info("update etherrain switch {0} - {1}".format( - # self._valve_id, self._state)) - - @property - def is_on(self): - """Return valve state.""" - # _LOGGER.info("is_on: etherrain switch {0} - {1}".format( - # self._valve_id, self._state)) - return self._state From 783974acb2d55f81c91df79f150512a6e5c50c6c Mon Sep 17 00:00:00 2001 From: Herb Peyerl Date: Sun, 13 Aug 2017 06:43:05 -0600 Subject: [PATCH 15/15] disable some more logging --- homeassistant/components/etherrain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/etherrain.py b/homeassistant/components/etherrain.py index a1d132c63768e3..2ff527aeb9bbc1 100644 --- a/homeassistant/components/etherrain.py +++ b/homeassistant/components/etherrain.py @@ -91,7 +91,7 @@ def _er_request(data=None): # _LOGGER.info("Water off".format(valve,duration)) url = '{0}/result.cgi?xr'.format(ER['server_origin']) if cmd == WATER_ON: - _LOGGER.info("Set {0} to {1} minutes".format(valve, duration)) + # _LOGGER.info("Set {0} to {1} minutes".format(valve, duration)) valves = ["0", "0", "0", "0", "0", "0", "0", "0", "0"] valves[valve] = duration url = '{0}/result.cgi?xi=0:{1}:{2}:{3}:{4}:{5}:{6}:{7}:{8}'.format( @@ -168,5 +168,5 @@ def get_state(valve): # pylint: disable=no-member def change_state(valve_data): """Change the state of a valve.""" - _LOGGER.info("Change State: {0}".format(valve_data)) + # _LOGGER.info("Change State: {0}".format(valve_data)) return _er_request(data=valve_data)