From f931cc3d1c22a65c5b73a7beedb59205af8b3094 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Mon, 12 Jun 2023 21:50:23 +0200 Subject: [PATCH] Fix manual update for Command Line (#94433) Manual update command line --- .../components/command_line/binary_sensor.py | 8 +++ .../components/command_line/cover.py | 9 ++- .../components/command_line/sensor.py | 8 +++ .../components/command_line/switch.py | 9 ++- .../command_line/test_binary_sensor.py | 59 ++++++++++++++++++- tests/components/command_line/test_cover.py | 58 ++++++++++++++++++ tests/components/command_line/test_sensor.py | 56 ++++++++++++++++++ tests/components/command_line/test_switch.py | 59 +++++++++++++++++++ 8 files changed, 263 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/command_line/binary_sensor.py b/homeassistant/components/command_line/binary_sensor.py index 06aa58ca068..fb8f57b4d5a 100644 --- a/homeassistant/components/command_line/binary_sensor.py +++ b/homeassistant/components/command_line/binary_sensor.py @@ -31,6 +31,7 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss from homeassistant.helpers.template import Template from homeassistant.helpers.template_entity import ManualTriggerEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.util import dt as dt_util from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN, LOGGER from .sensor import CommandSensorData @@ -185,3 +186,10 @@ class CommandBinarySensor(ManualTriggerEntity, BinarySensorEntity): self._process_manual_data(value) self.async_write_ha_state() + + async def async_update(self) -> None: + """Update the entity. + + Only used by the generic entity update service. + """ + await self._update_entity_state(dt_util.now()) diff --git a/homeassistant/components/command_line/cover.py b/homeassistant/components/command_line/cover.py index 29236bbed08..ebbc8a9b30b 100644 --- a/homeassistant/components/command_line/cover.py +++ b/homeassistant/components/command_line/cover.py @@ -32,7 +32,7 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss from homeassistant.helpers.template import Template from homeassistant.helpers.template_entity import ManualTriggerEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from homeassistant.util import slugify +from homeassistant.util import dt as dt_util, slugify from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN, LOGGER from .utils import call_shell_with_timeout, check_output_or_log @@ -224,6 +224,13 @@ class CommandCover(ManualTriggerEntity, CoverEntity): self._process_manual_data(payload) await self.async_update_ha_state(True) + async def async_update(self) -> None: + """Update the entity. + + Only used by the generic entity update service. + """ + await self._update_entity_state(dt_util.now()) + async def async_open_cover(self, **kwargs: Any) -> None: """Open the cover.""" await self.hass.async_add_executor_job(self._move_cover, self._command_open) diff --git a/homeassistant/components/command_line/sensor.py b/homeassistant/components/command_line/sensor.py index b9dffd3ca45..c164c6636fa 100644 --- a/homeassistant/components/command_line/sensor.py +++ b/homeassistant/components/command_line/sensor.py @@ -36,6 +36,7 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss from homeassistant.helpers.template import Template from homeassistant.helpers.template_entity import ManualTriggerEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from homeassistant.util import dt as dt_util from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN, LOGGER from .utils import check_output_or_log @@ -216,6 +217,13 @@ class CommandSensor(ManualTriggerEntity, SensorEntity): self._process_manual_data(value) self.async_write_ha_state() + async def async_update(self) -> None: + """Update the entity. + + Only used by the generic entity update service. + """ + await self._update_entity_state(dt_util.now()) + class CommandSensorData: """The class for handling the data retrieval.""" diff --git a/homeassistant/components/command_line/switch.py b/homeassistant/components/command_line/switch.py index 1a3dd39a342..4a33d8072d7 100644 --- a/homeassistant/components/command_line/switch.py +++ b/homeassistant/components/command_line/switch.py @@ -34,7 +34,7 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss from homeassistant.helpers.template import Template from homeassistant.helpers.template_entity import ManualTriggerEntity from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType -from homeassistant.util import slugify +from homeassistant.util import dt as dt_util, slugify from .const import CONF_COMMAND_TIMEOUT, DEFAULT_TIMEOUT, DOMAIN, LOGGER from .utils import call_shell_with_timeout, check_output_or_log @@ -240,6 +240,13 @@ class CommandSwitch(ManualTriggerEntity, SwitchEntity): self._process_manual_data(payload) await self.async_update_ha_state(True) + async def async_update(self) -> None: + """Update the entity. + + Only used by the generic entity update service. + """ + await self._update_entity_state(dt_util.now()) + async def async_turn_on(self, **kwargs: Any) -> None: """Turn the device on.""" if await self._switch(self._command_on) and not self._command_state: diff --git a/tests/components/command_line/test_binary_sensor.py b/tests/components/command_line/test_binary_sensor.py index eb6b52a66be..9e97f053e07 100644 --- a/tests/components/command_line/test_binary_sensor.py +++ b/tests/components/command_line/test_binary_sensor.py @@ -12,7 +12,11 @@ from homeassistant import setup from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.command_line.binary_sensor import CommandBinarySensor from homeassistant.components.command_line.const import DOMAIN -from homeassistant.const import STATE_OFF, STATE_ON +from homeassistant.components.homeassistant import ( + DOMAIN as HA_DOMAIN, + SERVICE_UPDATE_ENTITY, +) +from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_ON from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er import homeassistant.helpers.issue_registry as ir @@ -252,3 +256,56 @@ async def test_updating_to_often( ) await asyncio.sleep(0.2) + + +async def test_updating_manually( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test handling manual updating using homeassistant udate_entity service.""" + await setup.async_setup_component(hass, HA_DOMAIN, {}) + called = [] + + class MockCommandBinarySensor(CommandBinarySensor): + """Mock entity that updates slow.""" + + async def _async_update(self) -> None: + """Update slow.""" + called.append(1) + # Add waiting time + await asyncio.sleep(1) + + with patch( + "homeassistant.components.command_line.binary_sensor.CommandBinarySensor", + side_effect=MockCommandBinarySensor, + ): + await setup.async_setup_component( + hass, + DOMAIN, + { + "command_line": [ + { + "binary_sensor": { + "name": "Test", + "command": "echo 1", + "payload_on": "1", + "payload_off": "0", + "scan_interval": 10, + } + } + ] + }, + ) + await hass.async_block_till_done() + + assert len(called) == 1 + + await hass.services.async_call( + HA_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: ["binary_sensor.test"]}, + blocking=True, + ) + await hass.async_block_till_done() + assert len(called) == 2 + + await asyncio.sleep(0.2) diff --git a/tests/components/command_line/test_cover.py b/tests/components/command_line/test_cover.py index d621d98c744..e0187b80f41 100644 --- a/tests/components/command_line/test_cover.py +++ b/tests/components/command_line/test_cover.py @@ -13,6 +13,10 @@ from homeassistant import config as hass_config, setup from homeassistant.components.command_line import DOMAIN from homeassistant.components.command_line.cover import CommandCover from homeassistant.components.cover import DOMAIN as COVER_DOMAIN, SCAN_INTERVAL +from homeassistant.components.homeassistant import ( + DOMAIN as HA_DOMAIN, + SERVICE_UPDATE_ENTITY, +) from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_CLOSE_COVER, @@ -378,3 +382,57 @@ async def test_updating_to_often( ) await asyncio.sleep(0.2) + + +async def test_updating_manually( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test handling manual updating using homeassistant udate_entity service.""" + await setup.async_setup_component(hass, HA_DOMAIN, {}) + called = [] + + class MockCommandCover(CommandCover): + """Mock entity that updates slow.""" + + async def _async_update(self) -> None: + """Update slow.""" + called.append(1) + # Add waiting time + await asyncio.sleep(1) + + with patch( + "homeassistant.components.command_line.cover.CommandCover", + side_effect=MockCommandCover, + ): + await setup.async_setup_component( + hass, + DOMAIN, + { + "command_line": [ + { + "cover": { + "command_state": "echo 1", + "value_template": "{{ value }}", + "name": "Test", + "scan_interval": 10, + } + } + ] + }, + ) + await hass.async_block_till_done() + + async_fire_time_changed(hass, dt_util.now() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert len(called) == 1 + + await hass.services.async_call( + HA_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: ["cover.test"]}, + blocking=True, + ) + await hass.async_block_till_done() + assert len(called) == 2 + + await asyncio.sleep(0.2) diff --git a/tests/components/command_line/test_sensor.py b/tests/components/command_line/test_sensor.py index 244a1b992ce..b837f580862 100644 --- a/tests/components/command_line/test_sensor.py +++ b/tests/components/command_line/test_sensor.py @@ -11,7 +11,12 @@ import pytest from homeassistant import setup from homeassistant.components.command_line import DOMAIN from homeassistant.components.command_line.sensor import CommandSensor +from homeassistant.components.homeassistant import ( + DOMAIN as HA_DOMAIN, + SERVICE_UPDATE_ENTITY, +) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import ATTR_ENTITY_ID from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er import homeassistant.helpers.issue_registry as ir @@ -586,3 +591,54 @@ async def test_updating_to_often( ) await asyncio.sleep(0.2) + + +async def test_updating_manually( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test handling manual updating using homeassistant udate_entity service.""" + await setup.async_setup_component(hass, HA_DOMAIN, {}) + called = [] + + class MockCommandSensor(CommandSensor): + """Mock entity that updates slow.""" + + async def _async_update(self) -> None: + """Update slow.""" + called.append(1) + # Add waiting time + await asyncio.sleep(1) + + with patch( + "homeassistant.components.command_line.sensor.CommandSensor", + side_effect=MockCommandSensor, + ): + await setup.async_setup_component( + hass, + DOMAIN, + { + "command_line": [ + { + "sensor": { + "name": "Test", + "command": "echo 1", + "scan_interval": 10, + } + } + ] + }, + ) + await hass.async_block_till_done() + + assert len(called) == 1 + + await hass.services.async_call( + HA_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: ["sensor.test"]}, + blocking=True, + ) + await hass.async_block_till_done() + assert len(called) == 2 + + await asyncio.sleep(0.2) diff --git a/tests/components/command_line/test_switch.py b/tests/components/command_line/test_switch.py index 88a87588375..e5331fbe7dd 100644 --- a/tests/components/command_line/test_switch.py +++ b/tests/components/command_line/test_switch.py @@ -14,6 +14,10 @@ import pytest from homeassistant import setup from homeassistant.components.command_line import DOMAIN from homeassistant.components.command_line.switch import CommandSwitch +from homeassistant.components.homeassistant import ( + DOMAIN as HA_DOMAIN, + SERVICE_UPDATE_ENTITY, +) from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN, SCAN_INTERVAL from homeassistant.const import ( ATTR_ENTITY_ID, @@ -696,3 +700,58 @@ async def test_updating_to_often( ) await asyncio.sleep(0.2) + + +async def test_updating_manually( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test handling manual updating using homeassistant udate_entity service.""" + await setup.async_setup_component(hass, HA_DOMAIN, {}) + called = [] + + class MockCommandSwitch(CommandSwitch): + """Mock entity that updates slow.""" + + async def _async_update(self) -> None: + """Update slow.""" + called.append(1) + # Add waiting time + await asyncio.sleep(1) + + with patch( + "homeassistant.components.command_line.switch.CommandSwitch", + side_effect=MockCommandSwitch, + ): + await setup.async_setup_component( + hass, + DOMAIN, + { + "command_line": [ + { + "switch": { + "command_state": "echo 1", + "command_on": "echo 2", + "command_off": "echo 3", + "name": "Test", + "scan_interval": 10, + } + } + ] + }, + ) + await hass.async_block_till_done() + + async_fire_time_changed(hass, dt_util.now() + timedelta(seconds=10)) + await hass.async_block_till_done() + assert len(called) == 1 + + await hass.services.async_call( + HA_DOMAIN, + SERVICE_UPDATE_ENTITY, + {ATTR_ENTITY_ID: ["switch.test"]}, + blocking=True, + ) + await hass.async_block_till_done() + assert len(called) == 2 + + await asyncio.sleep(0.2)