From 4531dbbe62885399998c712b78dcf6a973d4ef13 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Thu, 10 Aug 2023 12:59:23 +0200 Subject: [PATCH] Refactor Rest Binary sensor with ManualTriggerEntity (#97400) * Refactor Rest Binary sensor w/ ManualTriggerEntity * test availability * review comments * Use super * Fix config --- .../components/rest/binary_sensor.py | 51 ++++++++++++++----- homeassistant/components/rest/schema.py | 2 + homeassistant/helpers/template_entity.py | 1 + tests/components/rest/test_binary_sensor.py | 24 +++++++++ 4 files changed, 65 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/rest/binary_sensor.py b/homeassistant/components/rest/binary_sensor.py index 0c1f4df60930..7ab632995ea9 100644 --- a/homeassistant/components/rest/binary_sensor.py +++ b/homeassistant/components/rest/binary_sensor.py @@ -14,6 +14,8 @@ from homeassistant.components.binary_sensor import ( from homeassistant.const import ( CONF_DEVICE_CLASS, CONF_FORCE_UPDATE, + CONF_ICON, + CONF_NAME, CONF_RESOURCE, CONF_RESOURCE_TEMPLATE, CONF_UNIQUE_ID, @@ -24,7 +26,11 @@ from homeassistant.exceptions import PlatformNotReady import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.template import Template -from homeassistant.helpers.template_entity import TemplateEntity +from homeassistant.helpers.template_entity import ( + CONF_AVAILABILITY, + CONF_PICTURE, + ManualTriggerEntity, +) from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.update_coordinator import DataUpdateCoordinator @@ -42,6 +48,14 @@ PLATFORM_SCHEMA = vol.All( cv.has_at_least_one_key(CONF_RESOURCE, CONF_RESOURCE_TEMPLATE), PLATFORM_SCHEMA ) +TRIGGER_ENTITY_OPTIONS = ( + CONF_AVAILABILITY, + CONF_DEVICE_CLASS, + CONF_ICON, + CONF_PICTURE, + CONF_UNIQUE_ID, +) + async def async_setup_platform( hass: HomeAssistant, @@ -74,7 +88,14 @@ async def async_setup_platform( raise PlatformNotReady from rest.last_exception raise PlatformNotReady - unique_id = conf.get(CONF_UNIQUE_ID) + name = conf.get(CONF_NAME) or Template(DEFAULT_BINARY_SENSOR_NAME, hass) + + trigger_entity_config = {CONF_NAME: name} + + for key in TRIGGER_ENTITY_OPTIONS: + if key not in conf: + continue + trigger_entity_config[key] = conf[key] async_add_entities( [ @@ -83,13 +104,13 @@ async def async_setup_platform( coordinator, rest, conf, - unique_id, + trigger_entity_config, ) ], ) -class RestBinarySensor(RestEntity, TemplateEntity, BinarySensorEntity): +class RestBinarySensor(ManualTriggerEntity, RestEntity, BinarySensorEntity): """Representation of a REST binary sensor.""" def __init__( @@ -98,9 +119,10 @@ class RestBinarySensor(RestEntity, TemplateEntity, BinarySensorEntity): coordinator: DataUpdateCoordinator[None] | None, rest: RestData, config: ConfigType, - unique_id: str | None, + trigger_entity_config: ConfigType, ) -> None: """Initialize a REST binary sensor.""" + ManualTriggerEntity.__init__(self, hass, trigger_entity_config) RestEntity.__init__( self, coordinator, @@ -108,19 +130,17 @@ class RestBinarySensor(RestEntity, TemplateEntity, BinarySensorEntity): config.get(CONF_RESOURCE_TEMPLATE), config[CONF_FORCE_UPDATE], ) - TemplateEntity.__init__( - self, - hass, - config=config, - fallback_name=DEFAULT_BINARY_SENSOR_NAME, - unique_id=unique_id, - ) self._previous_data = None self._value_template: Template | None = config.get(CONF_VALUE_TEMPLATE) if (value_template := self._value_template) is not None: value_template.hass = hass - self._attr_device_class = config.get(CONF_DEVICE_CLASS) + @property + def available(self) -> bool: + """Return if entity is available.""" + available1 = RestEntity.available.fget(self) # type: ignore[attr-defined] + available2 = ManualTriggerEntity.available.fget(self) # type: ignore[attr-defined] + return bool(available1 and available2) def _update_from_rest_data(self) -> None: """Update state from the rest data.""" @@ -130,6 +150,8 @@ class RestBinarySensor(RestEntity, TemplateEntity, BinarySensorEntity): response = self.rest.data + raw_value = response + if self._value_template is not None: response = self._value_template.async_render_with_possible_json_value( self.rest.data, False @@ -144,3 +166,6 @@ class RestBinarySensor(RestEntity, TemplateEntity, BinarySensorEntity): "open": True, "yes": True, }.get(response.lower(), False) + + self._process_manual_data(raw_value) + self.async_write_ha_state() diff --git a/homeassistant/components/rest/schema.py b/homeassistant/components/rest/schema.py index c5abe42d7fc9..c1f512866737 100644 --- a/homeassistant/components/rest/schema.py +++ b/homeassistant/components/rest/schema.py @@ -28,6 +28,7 @@ from homeassistant.const import ( ) import homeassistant.helpers.config_validation as cv from homeassistant.helpers.template_entity import ( + CONF_AVAILABILITY, TEMPLATE_ENTITY_BASE_SCHEMA, TEMPLATE_SENSOR_BASE_SCHEMA, ) @@ -82,6 +83,7 @@ BINARY_SENSOR_SCHEMA = { vol.Optional(CONF_DEVICE_CLASS): BINARY_SENSOR_DEVICE_CLASSES_SCHEMA, vol.Optional(CONF_VALUE_TEMPLATE): cv.template, vol.Optional(CONF_FORCE_UPDATE, default=DEFAULT_FORCE_UPDATE): cv.boolean, + vol.Optional(CONF_AVAILABILITY): cv.template, } diff --git a/homeassistant/helpers/template_entity.py b/homeassistant/helpers/template_entity.py index 2e5cebf8571a..07dd154922cb 100644 --- a/homeassistant/helpers/template_entity.py +++ b/homeassistant/helpers/template_entity.py @@ -564,6 +564,7 @@ class TriggerBaseEntity(Entity): async def async_added_to_hass(self) -> None: """Handle being added to Home Assistant.""" + await super().async_added_to_hass() template_attach(self.hass, self._config) def _set_unique_id(self, unique_id: str | None) -> None: diff --git a/tests/components/rest/test_binary_sensor.py b/tests/components/rest/test_binary_sensor.py index 86bac75de915..896f5544d93e 100644 --- a/tests/components/rest/test_binary_sensor.py +++ b/tests/components/rest/test_binary_sensor.py @@ -500,3 +500,27 @@ async def test_entity_config(hass: HomeAssistant) -> None: "friendly_name": "REST Binary Sensor", "icon": "mdi:one_two_three", } + + +@respx.mock +async def test_availability_in_config(hass: HomeAssistant) -> None: + """Test entity configuration.""" + + config = { + BINARY_SENSOR_DOMAIN: { + # REST configuration + "platform": DOMAIN, + "method": "GET", + "resource": "http://localhost", + # Entity configuration + "availability": "{{value==1}}", + "name": "{{'REST' + ' ' + 'Binary Sensor'}}", + }, + } + + respx.get("http://localhost") % HTTPStatus.OK + assert await async_setup_component(hass, BINARY_SENSOR_DOMAIN, config) + await hass.async_block_till_done() + + state = hass.states.get("binary_sensor.rest_binary_sensor") + assert state.state == STATE_UNAVAILABLE