From d6ab36bf8ec305e018c2da7d11aa75c19fb7a343 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 22 Apr 2020 23:29:49 +0200 Subject: [PATCH] Set mqtt binary_sensor unavailable if expire_after specified (#34259) * Set self._expired=True if expire_after specified * Added test_expiration_on_discovery_and_discovery_update_of_binary_sensor to mqtt/test_binary_sensor.py * Fixed flake8 error * Fixed isort error --- .../hap.cpython-37.pyc.139745820048096 | 0 .../components/mqtt/binary_sensor.py | 6 +- tests/components/mqtt/test_binary_sensor.py | 98 ++++++++++++++++++- 3 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 homeassistant/components/homematicip_cloud/__pycache__/hap.cpython-37.pyc.139745820048096 diff --git a/homeassistant/components/homematicip_cloud/__pycache__/hap.cpython-37.pyc.139745820048096 b/homeassistant/components/homematicip_cloud/__pycache__/hap.cpython-37.pyc.139745820048096 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/homeassistant/components/mqtt/binary_sensor.py b/homeassistant/components/mqtt/binary_sensor.py index c7595de0eeb..f07be49b421 100644 --- a/homeassistant/components/mqtt/binary_sensor.py +++ b/homeassistant/components/mqtt/binary_sensor.py @@ -119,7 +119,11 @@ class MqttBinarySensor( self._sub_state = None self._expiration_trigger = None self._delay_listener = None - self._expired = None + expire_after = config.get(CONF_EXPIRE_AFTER) + if expire_after is not None and expire_after > 0: + self._expired = True + else: + self._expired = None device_config = config.get(CONF_DEVICE) MqttAttributes.__init__(self, config) diff --git a/tests/components/mqtt/test_binary_sensor.py b/tests/components/mqtt/test_binary_sensor.py index 07a1c8b6e5f..11fb073f57a 100644 --- a/tests/components/mqtt/test_binary_sensor.py +++ b/tests/components/mqtt/test_binary_sensor.py @@ -4,7 +4,8 @@ from datetime import datetime, timedelta import json from unittest.mock import patch -from homeassistant.components import binary_sensor +from homeassistant.components import binary_sensor, mqtt +from homeassistant.components.mqtt.discovery import async_start from homeassistant.const import ( EVENT_STATE_CHANGED, STATE_OFF, @@ -37,7 +38,11 @@ from .test_common import ( help_test_update_with_json_attrs_not_dict, ) -from tests.common import async_fire_mqtt_message, async_fire_time_changed +from tests.common import ( + MockConfigEntry, + async_fire_mqtt_message, + async_fire_time_changed, +) DEFAULT_CONFIG = { binary_sensor.DOMAIN: { @@ -70,8 +75,9 @@ async def test_setting_sensor_value_expires_availability_topic(hass, mqtt_mock, async_fire_mqtt_message(hass, "availability-topic", "online") + # State should be unavailable since expire_after is defined and > 0 state = hass.states.get("binary_sensor.test") - assert state.state != STATE_UNAVAILABLE + assert state.state == STATE_UNAVAILABLE await expires_helper(hass, mqtt_mock, caplog) @@ -92,15 +98,15 @@ async def test_setting_sensor_value_expires(hass, mqtt_mock, caplog): }, ) + # State should be unavailable since expire_after is defined and > 0 state = hass.states.get("binary_sensor.test") - assert state.state == STATE_OFF + assert state.state == STATE_UNAVAILABLE await expires_helper(hass, mqtt_mock, caplog) async def expires_helper(hass, mqtt_mock, caplog): """Run the basic expiry code.""" - now = datetime(2017, 1, 1, 1, tzinfo=dt_util.UTC) with patch(("homeassistant.helpers.event.dt_util.utcnow"), return_value=now): async_fire_time_changed(hass, now) @@ -460,6 +466,88 @@ async def test_discovery_update_binary_sensor(hass, mqtt_mock, caplog): ) +async def test_expiration_on_discovery_and_discovery_update_of_binary_sensor( + hass, mqtt_mock, caplog +): + """Test that binary_sensor with expire_after set behaves correctly on discovery and discovery update.""" + entry = MockConfigEntry(domain=mqtt.DOMAIN) + await async_start(hass, "homeassistant", {}, entry) + + config = { + "name": "Test", + "state_topic": "test-topic", + "expire_after": 4, + "force_update": True, + } + + config_msg = json.dumps(config) + + # Set time and publish config message to create binary_sensor via discovery with 4 s expiry + now = datetime(2017, 1, 1, 1, tzinfo=dt_util.UTC) + with patch(("homeassistant.helpers.event.dt_util.utcnow"), return_value=now): + async_fire_time_changed(hass, now) + async_fire_mqtt_message( + hass, "homeassistant/binary_sensor/bla/config", config_msg + ) + await hass.async_block_till_done() + + # Test that binary_sensor is not available + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_UNAVAILABLE + + # Publish state message + with patch(("homeassistant.helpers.event.dt_util.utcnow"), return_value=now): + async_fire_mqtt_message(hass, "test-topic", "ON") + await hass.async_block_till_done() + + # Test that binary_sensor has correct state + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_ON + + # Advance +3 seconds + now = now + timedelta(seconds=3) + with patch(("homeassistant.helpers.event.dt_util.utcnow"), return_value=now): + async_fire_time_changed(hass, now) + await hass.async_block_till_done() + + # binary_sensor is not yet expired + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_ON + + # Resend config message to update discovery + with patch(("homeassistant.helpers.event.dt_util.utcnow"), return_value=now): + async_fire_time_changed(hass, now) + async_fire_mqtt_message( + hass, "homeassistant/binary_sensor/bla/config", config_msg + ) + await hass.async_block_till_done() + + # Test that binary_sensor has not expired + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_ON + + # Add +2 seconds + now = now + timedelta(seconds=2) + with patch(("homeassistant.helpers.event.dt_util.utcnow"), return_value=now): + async_fire_time_changed(hass, now) + await hass.async_block_till_done() + + # Test that binary_sensor has expired + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_UNAVAILABLE + + # Resend config message to update discovery + with patch(("homeassistant.helpers.event.dt_util.utcnow"), return_value=now): + async_fire_mqtt_message( + hass, "homeassistant/binary_sensor/bla/config", config_msg + ) + await hass.async_block_till_done() + + # Test that binary_sensor is still expired + state = hass.states.get("binary_sensor.test") + assert state.state == STATE_UNAVAILABLE + + async def test_discovery_broken(hass, mqtt_mock, caplog): """Test handling of bad discovery message.""" data1 = '{ "name": "Beer",' ' "off_delay": -1 }'