From a072047d9daad37fbe0d42e4ada34553744d082d Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Tue, 4 Oct 2016 10:07:17 +0200 Subject: [PATCH] Auth and headers support for REST sensor (#3592) * Add auth and header support * Update header part --- .../components/binary_sensor/rest.py | 27 ++++++++++++-- homeassistant/components/sensor/pi_hole.py | 4 ++- homeassistant/components/sensor/rest.py | 36 ++++++++++++++----- homeassistant/const.py | 1 + 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/homeassistant/components/binary_sensor/rest.py b/homeassistant/components/binary_sensor/rest.py index b8be340d43c..3e22150a4fd 100644 --- a/homeassistant/components/binary_sensor/rest.py +++ b/homeassistant/components/binary_sensor/rest.py @@ -5,15 +5,19 @@ For more details about this platform, please refer to the documentation at https://home-assistant.io/components/binary_sensor.rest/ """ import logging +import json import voluptuous as vol +from requests.auth import HTTPBasicAuth, HTTPDigestAuth from homeassistant.components.binary_sensor import ( BinarySensorDevice, SENSOR_CLASSES_SCHEMA, PLATFORM_SCHEMA) from homeassistant.components.sensor.rest import RestData from homeassistant.const import ( CONF_PAYLOAD, CONF_NAME, CONF_VALUE_TEMPLATE, CONF_METHOD, CONF_RESOURCE, - CONF_SENSOR_CLASS, CONF_VERIFY_SSL) + CONF_SENSOR_CLASS, CONF_VERIFY_SSL, CONF_USERNAME, CONF_PASSWORD, + CONF_HEADERS, CONF_AUTHENTICATION, HTTP_BASIC_AUTHENTICATION, + HTTP_DIGEST_AUTHENTICATION) import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) @@ -24,16 +28,21 @@ DEFAULT_VERIFY_SSL = True PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_RESOURCE): cv.url, + vol.Optional(CONF_AUTHENTICATION): + vol.In([HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION]), + vol.Optional(CONF_HEADERS): cv.string, vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): vol.In(['POST', 'GET']), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PASSWORD): cv.string, vol.Optional(CONF_PAYLOAD): cv.string, vol.Optional(CONF_SENSOR_CLASS): SENSOR_CLASSES_SCHEMA, + vol.Optional(CONF_USERNAME): cv.string, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, }) -# pylint: disable=unused-variable +# pylint: disable=unused-variable, too-many-locals def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the REST binary sensor.""" name = config.get(CONF_NAME) @@ -41,11 +50,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None): method = config.get(CONF_METHOD) payload = config.get(CONF_PAYLOAD) verify_ssl = config.get(CONF_VERIFY_SSL) + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + headers = json.loads(config.get(CONF_HEADERS, '{}')) sensor_class = config.get(CONF_SENSOR_CLASS) value_template = config.get(CONF_VALUE_TEMPLATE) if value_template is not None: value_template.hass = hass - rest = RestData(method, resource, payload, verify_ssl) + + if username and password: + if config.get(CONF_AUTHENTICATION) == HTTP_DIGEST_AUTHENTICATION: + auth = HTTPDigestAuth(username, password) + else: + auth = HTTPBasicAuth(username, password) + else: + auth = None + + rest = RestData(method, resource, auth, headers, payload, verify_ssl) rest.update() if rest.data is None: diff --git a/homeassistant/components/sensor/pi_hole.py b/homeassistant/components/sensor/pi_hole.py index 7cd3423bf65..c6ffdcaf64c 100644 --- a/homeassistant/components/sensor/pi_hole.py +++ b/homeassistant/components/sensor/pi_hole.py @@ -43,6 +43,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None): host = config.get(CONF_HOST) method = 'GET' payload = None + auth = None + headers = None verify_ssl = config.get(CONF_VERIFY_SSL) use_ssl = config.get(CONF_SSL) @@ -53,7 +55,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None): resource = "{}{}{}".format(uri_scheme, host, _ENDPOINT) - rest = RestData(method, resource, payload, verify_ssl) + rest = RestData(method, resource, auth, headers, payload, verify_ssl) rest.update() if rest.data is None: diff --git a/homeassistant/components/sensor/rest.py b/homeassistant/components/sensor/rest.py index edd5dc44865..cb856ae992a 100644 --- a/homeassistant/components/sensor/rest.py +++ b/homeassistant/components/sensor/rest.py @@ -1,5 +1,5 @@ """ -Support for REST API sensors. +Support for RESTful API sensors. For more details about this platform, please refer to the documentation at https://home-assistant.io/components/sensor.rest/ @@ -8,11 +8,14 @@ import logging import voluptuous as vol import requests +from requests.auth import HTTPBasicAuth, HTTPDigestAuth from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.const import ( CONF_PAYLOAD, CONF_NAME, CONF_VALUE_TEMPLATE, CONF_METHOD, CONF_RESOURCE, - CONF_UNIT_OF_MEASUREMENT, STATE_UNKNOWN, CONF_VERIFY_SSL) + CONF_UNIT_OF_MEASUREMENT, STATE_UNKNOWN, CONF_VERIFY_SSL, CONF_USERNAME, + CONF_PASSWORD, CONF_AUTHENTICATION, HTTP_BASIC_AUTHENTICATION, + HTTP_DIGEST_AUTHENTICATION, CONF_HEADERS) from homeassistant.helpers.entity import Entity import homeassistant.helpers.config_validation as cv @@ -24,16 +27,21 @@ DEFAULT_VERIFY_SSL = True PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_RESOURCE): cv.url, + vol.Optional(CONF_AUTHENTICATION): + vol.In([HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION]), + vol.Optional(CONF_HEADERS): {cv.string: cv.string}, vol.Optional(CONF_METHOD, default=DEFAULT_METHOD): vol.In(['POST', 'GET']), vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, + vol.Optional(CONF_PASSWORD): cv.string, vol.Optional(CONF_PAYLOAD): cv.string, vol.Optional(CONF_UNIT_OF_MEASUREMENT): cv.string, + vol.Optional(CONF_USERNAME): cv.string, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, }) -# pylint: disable=unused-variable +# pylint: disable=unused-variable, too-many-locals def setup_platform(hass, config, add_devices, discovery_info=None): """Setup the RESTful sensor.""" name = config.get(CONF_NAME) @@ -41,11 +49,22 @@ def setup_platform(hass, config, add_devices, discovery_info=None): method = config.get(CONF_METHOD) payload = config.get(CONF_PAYLOAD) verify_ssl = config.get(CONF_VERIFY_SSL) + username = config.get(CONF_USERNAME) + password = config.get(CONF_PASSWORD) + headers = config.get(CONF_HEADERS) unit = config.get(CONF_UNIT_OF_MEASUREMENT) value_template = config.get(CONF_VALUE_TEMPLATE) if value_template is not None: value_template.hass = hass - rest = RestData(method, resource, payload, verify_ssl) + + if username and password: + if config.get(CONF_AUTHENTICATION) == HTTP_DIGEST_AUTHENTICATION: + auth = HTTPDigestAuth(username, password) + else: + auth = HTTPBasicAuth(username, password) + else: + auth = None + rest = RestData(method, resource, auth, headers, payload, verify_ssl) rest.update() if rest.data is None: @@ -102,9 +121,10 @@ class RestSensor(Entity): class RestData(object): """Class for handling the data retrieval.""" - def __init__(self, method, resource, data, verify_ssl): + def __init__(self, method, resource, auth, headers, data, verify_ssl): """Initialize the data object.""" - self._request = requests.Request(method, resource, data=data).prepare() + self._request = requests.Request( + method, resource, headers=headers, auth=auth, data=data).prepare() self._verify_ssl = verify_ssl self.data = None @@ -112,8 +132,8 @@ class RestData(object): """Get the latest data from REST service with GET method.""" try: with requests.Session() as sess: - response = sess.send(self._request, timeout=10, - verify=self._verify_ssl) + response = sess.send( + self._request, timeout=10, verify=self._verify_ssl) self.data = response.text except requests.exceptions.RequestException: diff --git a/homeassistant/const.py b/homeassistant/const.py index b8cd3a83353..3c8c74d5bad 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -87,6 +87,7 @@ CONF_EVENT = 'event' CONF_FILE_PATH = 'file_path' CONF_FILENAME = 'filename' CONF_FRIENDLY_NAME = 'friendly_name' +CONF_HEADERS = 'headers' CONF_HOST = 'host' CONF_HOSTS = 'hosts' CONF_ICON = 'icon'