diff --git a/homeassistant/components/rest/binary_sensor.py b/homeassistant/components/rest/binary_sensor.py index 6d797dfd834..7efcf2a3557 100644 --- a/homeassistant/components/rest/binary_sensor.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -7,17 +7,19 @@ import voluptuous as vol from homeassistant.components.binary_sensor import ( DEVICE_CLASSES_SCHEMA, PLATFORM_SCHEMA, - BinarySensorEntity, + BinarySensorDevice, ) from homeassistant.const import ( CONF_AUTHENTICATION, CONF_DEVICE_CLASS, + CONF_FORCE_UPDATE, CONF_HEADERS, CONF_METHOD, CONF_NAME, CONF_PASSWORD, CONF_PAYLOAD, CONF_RESOURCE, + CONF_RESOURCE_TEMPLATE, CONF_TIMEOUT, CONF_USERNAME, CONF_VALUE_TEMPLATE, @@ -35,11 +37,13 @@ _LOGGER = logging.getLogger(__name__) DEFAULT_METHOD = "GET" DEFAULT_NAME = "REST Binary Sensor" DEFAULT_VERIFY_SSL = True +DEFAULT_FORCE_UPDATE = False DEFAULT_TIMEOUT = 10 PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { - vol.Required(CONF_RESOURCE): cv.url, + vol.Exclusive(CONF_RESOURCE, CONF_RESOURCE): cv.url, + vol.Exclusive(CONF_RESOURCE_TEMPLATE, CONF_RESOURCE): cv.template, vol.Optional(CONF_AUTHENTICATION): vol.In( [HTTP_BASIC_AUTHENTICATION, HTTP_DIGEST_AUTHENTICATION] ), @@ -52,15 +56,21 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( vol.Optional(CONF_USERNAME): cv.string, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_VERIFY_SSL, default=DEFAULT_VERIFY_SSL): cv.boolean, + vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int, } ) +PLATFORM_SCHEMA = vol.All( + cv.has_at_least_one_key(CONF_RESOURCE, CONF_RESOURCE_TEMPLATE), PLATFORM_SCHEMA +) + def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the REST binary sensor.""" name = config.get(CONF_NAME) resource = config.get(CONF_RESOURCE) + resource_template = config.get(CONF_RESOURCE_TEMPLATE) method = config.get(CONF_METHOD) payload = config.get(CONF_PAYLOAD) verify_ssl = config.get(CONF_VERIFY_SSL) @@ -70,6 +80,12 @@ def setup_platform(hass, config, add_entities, discovery_info=None): headers = config.get(CONF_HEADERS) device_class = config.get(CONF_DEVICE_CLASS) value_template = config.get(CONF_VALUE_TEMPLATE) + force_update = config.get(CONF_FORCE_UPDATE) + + if resource_template is not None: + resource_template.hass = hass + resource = resource_template.render() + if value_template is not None: value_template.hass = hass @@ -86,15 +102,34 @@ def setup_platform(hass, config, add_entities, discovery_info=None): if rest.data is None: raise PlatformNotReady - # No need to update the sensor now because it will determine its state - # based in the rest resource that has just been retrieved. - add_entities([RestBinarySensor(hass, rest, name, device_class, value_template)]) + add_entities( + [ + RestBinarySensor( + hass, + rest, + name, + device_class, + value_template, + force_update, + resource_template, + ) + ] + ) -class RestBinarySensor(BinarySensorEntity): +class RestBinarySensor(BinarySensorDevice): """Representation of a REST binary sensor.""" - def __init__(self, hass, rest, name, device_class, value_template): + def __init__( + self, + hass, + rest, + name, + device_class, + value_template, + force_update, + resource_template, + ): """Initialize a REST binary sensor.""" self._hass = hass self.rest = rest @@ -103,6 +138,8 @@ class RestBinarySensor(BinarySensorEntity): self._state = False self._previous_data = None self._value_template = value_template + self._force_update = force_update + self._resource_template = resource_template @property def name(self): @@ -139,6 +176,14 @@ class RestBinarySensor(BinarySensorEntity): response.lower(), False ) + @property + def force_update(self): + """Force update.""" + return self._force_update + def update(self): """Get the latest data from REST API and updates the state.""" + if self._resource_template is not None: + self.rest.set_url(self._resource_template.render()) + self.rest.update() diff --git a/tests/components/rest/test_binary_sensor.py b/tests/components/rest/test_binary_sensor.py index 4d893a2ef64..e56342d861f 100644 --- a/tests/components/rest/test_binary_sensor.py +++ b/tests/components/rest/test_binary_sensor.py @@ -88,6 +88,42 @@ class TestRestBinarySensorSetup(unittest.TestCase): self.hass.block_till_done() assert 1 == mock_req.call_count + @requests_mock.Mocker() + def test_setup_minimum_resource_template(self, mock_req): + """Test setup with minimum configuration (resource_template).""" + mock_req.get("http://localhost", status_code=200) + with assert_setup_component(1, "binary_sensor"): + assert setup_component( + self.hass, + "binary_sensor", + { + "binary_sensor": { + "platform": "rest", + "resource_template": "http://localhost", + } + }, + ) + self.hass.block_till_done() + assert mock_req.call_count == 1 + + @requests_mock.Mocker() + def test_setup_duplicate_resource(self, mock_req): + """Test setup with duplicate resources.""" + mock_req.get("http://localhost", status_code=200) + with assert_setup_component(0, "binary_sensor"): + assert setup_component( + self.hass, + "binary_sensor", + { + "binary_sensor": { + "platform": "rest", + "resource": "http://localhost", + "resource_template": "http://localhost", + } + }, + ) + self.hass.block_till_done() + @requests_mock.Mocker() def test_setup_get(self, mock_req): """Test setup with valid configuration.""" @@ -155,9 +191,17 @@ class TestRestBinarySensor(unittest.TestCase): self.name = "foo" self.device_class = "light" self.value_template = template.Template("{{ value_json.key }}", self.hass) + self.force_update = False + self.resource_template = None self.binary_sensor = rest.RestBinarySensor( - self.hass, self.rest, self.name, self.device_class, self.value_template + self.hass, + self.rest, + self.name, + self.device_class, + self.value_template, + self.force_update, + self.resource_template, ) self.addCleanup(self.hass.stop) @@ -210,7 +254,13 @@ class TestRestBinarySensor(unittest.TestCase): "rest.RestData.update", side_effect=self.update_side_effect("true") ) self.binary_sensor = rest.RestBinarySensor( - self.hass, self.rest, self.name, self.device_class, None + self.hass, + self.rest, + self.name, + self.device_class, + None, + self.force_update, + self.resource_template, ) self.binary_sensor.update() assert STATE_ON == self.binary_sensor.state