From 1c3fa89914130667ffcddfefe25157091900ea9c Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 5 Sep 2015 13:09:55 +0200 Subject: [PATCH 1/3] add arest sensor --- homeassistant/components/sensor/arest.py | 139 +++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 homeassistant/components/sensor/arest.py diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py new file mode 100644 index 00000000000..6f2cb457838 --- /dev/null +++ b/homeassistant/components/sensor/arest.py @@ -0,0 +1,139 @@ +""" +homeassistant.components.sensor.arest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The arest sensor will consume an exposed aREST API of a device. + +Configuration: + +To use the arest sensor you will need to add something like the following +to your configuration.yaml file. + +sensor: + platform: arest + resource: http://IP_ADDRESS + monitored_variables: + - name: temperature + unit: '°C' + - name: humidity + unit: '%' + +Variables: + +resource: +*Required +IP address of the device that is exposing an aREST API. + +These are the variables for the monitored_variables array: + +name +*Required +The name of the variable you wish to monitor. + +unit +*Optional +Defines the units of measurement of the sensor, if any. + +Details for the API : http://arest.io + +Format of a default JSON response by aREST: +{ + "variables":{ + "temperature":21, + "humidity":89 + }, + "id":"device008", + "name":"Bedroom", + "connected":true +} +""" +import logging +from requests import get, exceptions + +from homeassistant.helpers.entity import Entity + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Get the aREST sensor. """ + + resource = config.get('resource', None) + + try: + response = get(resource) + except exceptions.MissingSchema: + _LOGGER.error("Missing schema in configuration. " + "Add http:// to your URL.") + return False + except exceptions.ConnectionError: + _LOGGER.error("No route to device. " + "Please check the IP address in the configuration file.") + return False + + data = ArestData(resource) + + dev = [] + for variable in config['monitored_variables']: + if 'unit' not in variable: + variable['unit'] = ' ' + if variable['name'] not in response.json()['variables']: + _LOGGER.error('Variable: "%s" does not exist', variable['name']) + else: + dev.append(ArestSensor(data, + response.json()['name'], + variable['name'], + variable['unit'])) + + add_devices(dev) + + +class ArestSensor(Entity): + """ Implements an aREST sensor. """ + + def __init__(self, data, location, variable, unit_of_measurement): + self._data = data + self._name = '{} {}'.format(location.title(), variable.title()) + self._variable = variable + self._state = 'n/a' + self._unit_of_measurement = unit_of_measurement + self.update() + + @property + def name(self): + """ The name of the sensor. """ + return self._name + + @property + def unit_of_measurement(self): + """ Unit the value is expressed in. """ + return self._unit_of_measurement + + @property + def state(self): + """ Returns the state of the device. """ + return self._state + + def update(self): + """ Gets the latest data from aREST API and updates the states. """ + values = self._data.update() + if values is not None: + self._state = values[self._variable] + else: + self._state = 'n/a' + + +# pylint: disable=too-few-public-methods +class ArestData(object): + """ Class for handling the data retrieval. """ + + def __init__(self, resource): + self.resource = resource + + def update(self): + """ Gets the latest data from aREST API. """ + try: + response = get(self.resource) + return response.json()['variables'] + except exceptions.ConnectionError: + _LOGGER.error("No route to device. Is device offline?") + return None From 72426e08b87225a7785d9fb141d79b650cac0b5f Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sat, 5 Sep 2015 13:26:29 +0200 Subject: [PATCH 2/3] update errror message --- homeassistant/components/sensor/arest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index 6f2cb457838..1e8283379b5 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -62,7 +62,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): try: response = get(resource) except exceptions.MissingSchema: - _LOGGER.error("Missing schema in configuration. " + _LOGGER.error("Missing resource or schema in configuration. " "Add http:// to your URL.") return False except exceptions.ConnectionError: From 1a88e489869f2738da7ed64fb12b0136784b1178 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Sun, 6 Sep 2015 23:41:01 +0200 Subject: [PATCH 3/3] add throttle and other minor improvements --- homeassistant/components/sensor/arest.py | 37 +++++++++++++++--------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/sensor/arest.py b/homeassistant/components/sensor/arest.py index 1e8283379b5..78d173ef283 100644 --- a/homeassistant/components/sensor/arest.py +++ b/homeassistant/components/sensor/arest.py @@ -33,7 +33,7 @@ unit *Optional Defines the units of measurement of the sensor, if any. -Details for the API : http://arest.io +Details for the API: http://arest.io Format of a default JSON response by aREST: { @@ -48,11 +48,16 @@ Format of a default JSON response by aREST: """ import logging from requests import get, exceptions +from datetime import timedelta +from homeassistant.util import Throttle from homeassistant.helpers.entity import Entity _LOGGER = logging.getLogger(__name__) +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) + def setup_platform(hass, config, add_devices, discovery_info=None): """ Get the aREST sensor. """ @@ -70,7 +75,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): "Please check the IP address in the configuration file.") return False - data = ArestData(resource) + rest = ArestData(resource) dev = [] for variable in config['monitored_variables']: @@ -79,7 +84,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): if variable['name'] not in response.json()['variables']: _LOGGER.error('Variable: "%s" does not exist', variable['name']) else: - dev.append(ArestSensor(data, + dev.append(ArestSensor(rest, response.json()['name'], variable['name'], variable['unit'])) @@ -90,8 +95,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): class ArestSensor(Entity): """ Implements an aREST sensor. """ - def __init__(self, data, location, variable, unit_of_measurement): - self._data = data + def __init__(self, rest, location, variable, unit_of_measurement): + self.rest = rest self._name = '{} {}'.format(location.title(), variable.title()) self._variable = variable self._state = 'n/a' @@ -114,12 +119,14 @@ class ArestSensor(Entity): return self._state def update(self): - """ Gets the latest data from aREST API and updates the states. """ - values = self._data.update() - if values is not None: - self._state = values[self._variable] + """ Gets the latest data from aREST API and updates the state. """ + self.rest.update() + values = self.rest.data + + if 'error' in values: + self._state = values['error'] else: - self._state = 'n/a' + self._state = values[self._variable] # pylint: disable=too-few-public-methods @@ -128,12 +135,16 @@ class ArestData(object): def __init__(self, resource): self.resource = resource + self.data = dict() + @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): - """ Gets the latest data from aREST API. """ + """ Gets the latest data from aREST device. """ try: response = get(self.resource) - return response.json()['variables'] + if 'error' in self.data: + del self.data['error'] + self.data = response.json()['variables'] except exceptions.ConnectionError: _LOGGER.error("No route to device. Is device offline?") - return None + self.data['error'] = 'n/a'