diff --git a/homeassistant/components/sensor/rest.py b/homeassistant/components/sensor/rest.py new file mode 100644 index 00000000000..4779c38e232 --- /dev/null +++ b/homeassistant/components/sensor/rest.py @@ -0,0 +1,141 @@ +""" +homeassistant.components.sensor.rest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The rest sensor will consume JSON responses sent by an exposed REST API. + +Configuration: + +To use the rest sensor you will need to add something like the following +to your configuration.yaml file. + +sensor: + platform: arest + name: REST sensor + resource: http://IP_ADDRESS/ENDPOINT + variable: temperature + unit: '°C' + +Variables: + +name +*Optional +The name of the sensor. Default is 'REST Sensor'. + +resource +*Required +The full URL of the REST service/endpoint that provide the JSON response. + +variable +*Required +The name of the variable inside the JSON response you want to monitor. + +unit +*Optional +Defines the units of measurement of the sensor, if any. + +For more details about this platform, please refer to the documentation at +https://home-assistant.io/components/sensor.rest.html +""" +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__) + +DEFAULT_NAME = "REST Sensor" + +# Return cached results if last scan was less then this time ago +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) + + +# pylint: disable=unused-variable +def setup_platform(hass, config, add_devices, discovery_info=None): + """ Get the REST sensor. """ + + resource = config.get('resource', None) + + try: + response = get(resource, timeout=10) + except exceptions.MissingSchema: + _LOGGER.error("Missing resource or schema in configuration. " + "Add http:// to your URL.") + return False + except exceptions.ConnectionError: + _LOGGER.error("No route to resource/endpoint. " + "Please check the URL in the configuration file.") + return False + + rest = RestData(resource) + + dev = [] + add_devices([RestSensor(rest, + config.get('name', DEFAULT_NAME), + config.get('variable'), + config.get('unit'))]) + add_devices(dev) + + +class RestSensor(Entity): + """ Implements a REST sensor. """ + + def __init__(self, rest, name, variable, unit_of_measurement): + self.rest = rest + self._name = name + 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 REST API and updates the state. """ + self.rest.update() + value = self.rest.data + + if 'error' in value: + self._state = value['error'] + else: + try: + self._state = value[self._variable] + except KeyError: + _LOGGER.error('Variable "%s" not found in response: "%s".', + self._variable, value) + self._state = 'N/A' + + +# pylint: disable=too-few-public-methods +class RestData(object): + """ Class for handling the data retrieval. """ + + def __init__(self, resource): + self.resource = resource + self.data = dict() + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """ Gets the latest data from REST service. """ + try: + response = get(self.resource, timeout=10) + if 'error' in self.data: + del self.data['error'] + self.data = response.json() + except exceptions.ConnectionError: + _LOGGER.error("No route to resource/endpoint.") + self.data['error'] = 'N/A'