diff --git a/homeassistant/components/api/__init__.py b/homeassistant/components/api/__init__.py index e40a9332c38..47c6518f7b1 100644 --- a/homeassistant/components/api/__init__.py +++ b/homeassistant/components/api/__init__.py @@ -37,7 +37,6 @@ from homeassistant.helpers import template from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers.network import NoURLAvailableError, get_url from homeassistant.helpers.service import async_get_all_descriptions -from homeassistant.helpers.state import AsyncTrackStates from homeassistant.helpers.system_info import async_get_system_info _LOGGER = logging.getLogger(__name__) @@ -367,20 +366,27 @@ class APIDomainServicesView(HomeAssistantView): Returns a list of changed states. """ - hass = request.app["hass"] + hass: ha.HomeAssistant = request.app["hass"] body = await request.text() try: data = json.loads(body) if body else None except ValueError: return self.json_message("Data should be valid JSON.", HTTP_BAD_REQUEST) - with AsyncTrackStates(hass) as changed_states: - try: - await hass.services.async_call( - domain, service, data, blocking=True, context=self.context(request) - ) - except (vol.Invalid, ServiceNotFound) as ex: - raise HTTPBadRequest() from ex + context = self.context(request) + + try: + await hass.services.async_call( + domain, service, data, blocking=True, context=context + ) + except (vol.Invalid, ServiceNotFound) as ex: + raise HTTPBadRequest() from ex + + changed_states = [] + + for state in hass.states.async_all(): + if state.context is context: + changed_states.append(state) return self.json(changed_states) diff --git a/homeassistant/helpers/state.py b/homeassistant/helpers/state.py index 87112cd9133..eda026c8563 100644 --- a/homeassistant/helpers/state.py +++ b/homeassistant/helpers/state.py @@ -22,6 +22,7 @@ from homeassistant.core import Context, State from homeassistant.loader import IntegrationNotFound, async_get_integration, bind_hass import homeassistant.util.dt as dt_util +from .frame import report from .typing import HomeAssistantType _LOGGER = logging.getLogger(__name__) @@ -35,6 +36,9 @@ class AsyncTrackStates: when with-block is exited. Must be run within the event loop. + + Deprecated. Remove after June 2021. + Warning added via `get_changed_since`. """ def __init__(self, hass: HomeAssistantType) -> None: @@ -61,7 +65,11 @@ class AsyncTrackStates: def get_changed_since( states: Iterable[State], utc_point_in_time: dt.datetime ) -> List[State]: - """Return list of states that have been changed since utc_point_in_time.""" + """Return list of states that have been changed since utc_point_in_time. + + Deprecated. Remove after June 2021. + """ + report("uses deprecated `get_changed_since`") return [state for state in states if state.last_updated >= utc_point_in_time] diff --git a/tests/components/api/test_init.py b/tests/components/api/test_init.py index 678a8096af5..ffda908a29b 100644 --- a/tests/components/api/test_init.py +++ b/tests/components/api/test_init.py @@ -270,7 +270,6 @@ async def test_api_call_service_no_data(hass, mock_api_client): async def test_api_call_service_with_data(hass, mock_api_client): """Test if the API allows us to call a service.""" - test_value = [] @ha.callback def listener(service_call): @@ -278,17 +277,24 @@ async def test_api_call_service_with_data(hass, mock_api_client): Also test if our data came through. """ - if "test" in service_call.data: - test_value.append(1) + hass.states.async_set( + "test.data", + "on", + {"data": service_call.data["test"]}, + context=service_call.context, + ) hass.services.async_register("test_domain", "test_service", listener) - await mock_api_client.post( + resp = await mock_api_client.post( "/api/services/test_domain/test_service", json={"test": 1} ) - - await hass.async_block_till_done() - assert len(test_value) == 1 + data = await resp.json() + assert len(data) == 1 + state = data[0] + assert state["entity_id"] == "test.data" + assert state["state"] == "on" + assert state["attributes"] == {"data": 1} async def test_api_template(hass, mock_api_client): diff --git a/tests/helpers/conftest.py b/tests/helpers/conftest.py new file mode 100644 index 00000000000..4b3b9bf465d --- /dev/null +++ b/tests/helpers/conftest.py @@ -0,0 +1,31 @@ +"""Fixtures for helpers.""" +from unittest.mock import Mock, patch + +import pytest + + +@pytest.fixture +def mock_integration_frame(): + """Mock as if we're calling code from inside an integration.""" + correct_frame = Mock( + filename="/home/paulus/homeassistant/components/hue/light.py", + lineno="23", + line="self.light.is_on", + ) + with patch( + "homeassistant.helpers.frame.extract_stack", + return_value=[ + Mock( + filename="/home/paulus/homeassistant/core.py", + lineno="23", + line="do_something()", + ), + correct_frame, + Mock( + filename="/home/paulus/aiohue/lights.py", + lineno="2", + line="something()", + ), + ], + ): + yield correct_frame diff --git a/tests/helpers/test_frame.py b/tests/helpers/test_frame.py index 7fc46b3699d..b198a16adb1 100644 --- a/tests/helpers/test_frame.py +++ b/tests/helpers/test_frame.py @@ -6,34 +6,13 @@ import pytest from homeassistant.helpers import frame -async def test_extract_frame_integration(caplog): +async def test_extract_frame_integration(caplog, mock_integration_frame): """Test extracting the current frame from integration context.""" - correct_frame = Mock( - filename="/home/paulus/homeassistant/components/hue/light.py", - lineno="23", - line="self.light.is_on", - ) - with patch( - "homeassistant.helpers.frame.extract_stack", - return_value=[ - Mock( - filename="/home/paulus/homeassistant/core.py", - lineno="23", - line="do_something()", - ), - correct_frame, - Mock( - filename="/home/paulus/aiohue/lights.py", - lineno="2", - line="something()", - ), - ], - ): - found_frame, integration, path = frame.get_integration_frame() + found_frame, integration, path = frame.get_integration_frame() assert integration == "hue" assert path == "homeassistant/components/" - assert found_frame == correct_frame + assert found_frame == mock_integration_frame async def test_extract_frame_integration_with_excluded_intergration(caplog): diff --git a/tests/helpers/test_state.py b/tests/helpers/test_state.py index 89b0f3c6850..aa1d148fbd7 100644 --- a/tests/helpers/test_state.py +++ b/tests/helpers/test_state.py @@ -25,7 +25,7 @@ from homeassistant.util import dt as dt_util from tests.common import async_mock_service -async def test_async_track_states(hass): +async def test_async_track_states(hass, mock_integration_frame): """Test AsyncTrackStates context manager.""" point1 = dt_util.utcnow() point2 = point1 + timedelta(seconds=5) @@ -82,7 +82,7 @@ async def test_call_to_component(hass): ) -async def test_get_changed_since(hass): +async def test_get_changed_since(hass, mock_integration_frame): """Test get_changed_since.""" point1 = dt_util.utcnow() point2 = point1 + timedelta(seconds=5)