From 5ef6f87ab988f06995c018a788f3022c4be21419 Mon Sep 17 00:00:00 2001 From: Matthias Alphart Date: Fri, 8 Jan 2021 11:50:02 +0100 Subject: [PATCH 1/6] Fix KNX cover state return open when unknown (#44926) --- homeassistant/components/knx/cover.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/knx/cover.py b/homeassistant/components/knx/cover.py index b88b1cfe86a..33da600976e 100644 --- a/homeassistant/components/knx/cover.py +++ b/homeassistant/components/knx/cover.py @@ -79,6 +79,9 @@ class KNXCover(KnxEntity, CoverEntity): @property def is_closed(self): """Return if the cover is closed.""" + # state shall be "unknown" when xknx travelcalculator is not initialized + if self._device.current_position() is None: + return None return self._device.is_closed() @property From 5dfe8e15e3a0e1567445dae16cf905666fa47b6e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 8 Jan 2021 02:07:50 -1000 Subject: [PATCH 2/6] Fix wait_template incorrectly matching falsey values (#44938) --- homeassistant/helpers/script.py | 15 ++----- tests/helpers/test_script.py | 77 +++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 20 deletions(-) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index 77c842a27fe..48a662e3a81 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -62,11 +62,7 @@ from homeassistant.core import ( callback, ) from homeassistant.helpers import condition, config_validation as cv, service, template -from homeassistant.helpers.event import ( - TrackTemplate, - async_call_later, - async_track_template_result, -) +from homeassistant.helpers.event import async_call_later, async_track_template from homeassistant.helpers.script_variables import ScriptVariables from homeassistant.helpers.trigger import ( async_initialize_triggers, @@ -359,7 +355,7 @@ class _ScriptRun: return @callback - def _async_script_wait(event, updates): + def async_script_wait(entity_id, from_s, to_s): """Handle script after template condition is true.""" self._variables["wait"] = { "remaining": to_context.remaining if to_context else delay, @@ -368,12 +364,9 @@ class _ScriptRun: done.set() to_context = None - info = async_track_template_result( - self._hass, - [TrackTemplate(wait_template, self._variables)], - _async_script_wait, + unsub = async_track_template( + self._hass, wait_template, async_script_wait, self._variables ) - unsub = info.async_remove self._changed() done = asyncio.Event() diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index c81ed681d42..72f4b06d91c 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -7,6 +7,7 @@ import logging from types import MappingProxyType from unittest import mock +from async_timeout import timeout import pytest import voluptuous as vol @@ -544,6 +545,41 @@ async def test_wait_basic(hass, action_type): assert script_obj.last_action is None +@pytest.mark.parametrize("action_type", ["template", "trigger"]) +async def test_wait_basic_times_out(hass, action_type): + """Test wait actions times out when the action does not happen.""" + wait_alias = "wait step" + action = {"alias": wait_alias} + if action_type == "template": + action["wait_template"] = "{{ states.switch.test.state == 'off' }}" + else: + action["wait_for_trigger"] = { + "platform": "state", + "entity_id": "switch.test", + "to": "off", + } + sequence = cv.SCRIPT_SCHEMA(action) + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + wait_started_flag = async_watch_for_action(script_obj, wait_alias) + timed_out = False + + try: + hass.states.async_set("switch.test", "on") + hass.async_create_task(script_obj.async_run(context=Context())) + await asyncio.wait_for(wait_started_flag.wait(), 1) + assert script_obj.is_running + assert script_obj.last_action == wait_alias + hass.states.async_set("switch.test", "not_on") + + with timeout(0.1): + await hass.async_block_till_done() + except asyncio.TimeoutError: + timed_out = True + await script_obj.async_stop() + + assert timed_out + + @pytest.mark.parametrize("action_type", ["template", "trigger"]) async def test_multiple_runs_wait(hass, action_type): """Test multiple runs with wait in script.""" @@ -782,30 +818,53 @@ async def test_wait_template_variables_in(hass): async def test_wait_template_with_utcnow(hass): """Test the wait template with utcnow.""" - sequence = cv.SCRIPT_SCHEMA({"wait_template": "{{ utcnow().hours == 12 }}"}) + sequence = cv.SCRIPT_SCHEMA({"wait_template": "{{ utcnow().hour == 12 }}"}) script_obj = script.Script(hass, sequence, "Test Name", "test_domain") wait_started_flag = async_watch_for_action(script_obj, "wait") - start_time = dt_util.utcnow() + timedelta(hours=24) + start_time = dt_util.utcnow().replace(minute=1) + timedelta(hours=48) try: hass.async_create_task(script_obj.async_run(context=Context())) - async_fire_time_changed(hass, start_time.replace(hour=5)) - assert not script_obj.is_running - async_fire_time_changed(hass, start_time.replace(hour=12)) - await asyncio.wait_for(wait_started_flag.wait(), 1) - assert script_obj.is_running + + match_time = start_time.replace(hour=12) + with patch("homeassistant.util.dt.utcnow", return_value=match_time): + async_fire_time_changed(hass, match_time) except (AssertionError, asyncio.TimeoutError): await script_obj.async_stop() raise else: - async_fire_time_changed(hass, start_time.replace(hour=3)) await hass.async_block_till_done() - assert not script_obj.is_running +async def test_wait_template_with_utcnow_no_match(hass): + """Test the wait template with utcnow that does not match.""" + sequence = cv.SCRIPT_SCHEMA({"wait_template": "{{ utcnow().hour == 12 }}"}) + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + wait_started_flag = async_watch_for_action(script_obj, "wait") + start_time = dt_util.utcnow().replace(minute=1) + timedelta(hours=48) + timed_out = False + + try: + hass.async_create_task(script_obj.async_run(context=Context())) + await asyncio.wait_for(wait_started_flag.wait(), 1) + assert script_obj.is_running + + non_maching_time = start_time.replace(hour=3) + with patch("homeassistant.util.dt.utcnow", return_value=non_maching_time): + async_fire_time_changed(hass, non_maching_time) + + with timeout(0.1): + await hass.async_block_till_done() + except asyncio.TimeoutError: + timed_out = True + await script_obj.async_stop() + + assert timed_out + + @pytest.mark.parametrize("mode", ["no_timeout", "timeout_finish", "timeout_not_finish"]) @pytest.mark.parametrize("action_type", ["template", "trigger"]) async def test_wait_variables_out(hass, mode, action_type): From 0e4c560f38b65d2b68b614dbc5050e3d7a802dc1 Mon Sep 17 00:00:00 2001 From: Sergio Oller Date: Fri, 8 Jan 2021 17:28:22 +0100 Subject: [PATCH 3/6] Disambiguate Supervisor HTTPUnauthorized on user/password validation (#44940) * Disambiguate HTTPUnauthorized on user/password validation The HA core API usually returns 401 when the request does not have proper authentication tokens or they have expired. However the user/password validation endpoint may also return 401 when the given user/password is invalid. The supervisor is currently unable to distinguish both scenarios, and it needs to. See https://github.com/home-assistant/supervisor/issues/2408 * Return 404 if user& password are not found/valid * Fix test for invalid user/password --- homeassistant/components/hassio/auth.py | 2 +- tests/components/hassio/test_auth.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/hassio/auth.py b/homeassistant/components/hassio/auth.py index 23b91ac40bc..a1c032fe0fe 100644 --- a/homeassistant/components/hassio/auth.py +++ b/homeassistant/components/hassio/auth.py @@ -82,7 +82,7 @@ class HassIOAuth(HassIOBaseAuth): data[ATTR_USERNAME], data[ATTR_PASSWORD] ) except auth_ha.InvalidAuth: - raise HTTPUnauthorized() from None + raise HTTPNotFound() from None return web.Response(status=HTTP_OK) diff --git a/tests/components/hassio/test_auth.py b/tests/components/hassio/test_auth.py index c5ac9df74b7..3d6a339082c 100644 --- a/tests/components/hassio/test_auth.py +++ b/tests/components/hassio/test_auth.py @@ -66,7 +66,7 @@ async def test_login_error(hass, hassio_client_supervisor): ) # Check we got right response - assert resp.status == 401 + assert resp.status == 404 mock_login.assert_called_with("test", "123456") From 1b0e0996af9fd16e5050757a0c59ad9ed674dde7 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Sat, 9 Jan 2021 01:10:47 +0100 Subject: [PATCH 4/6] Fix parameters when toggling light (#44950) --- homeassistant/components/light/__init__.py | 2 +- tests/components/light/test_init.py | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/light/__init__.py b/homeassistant/components/light/__init__.py index fdef5e61a76..f406366dc86 100644 --- a/homeassistant/components/light/__init__.py +++ b/homeassistant/components/light/__init__.py @@ -232,7 +232,7 @@ async def async_setup(hass, config): async def async_handle_toggle_service(light, call): """Handle toggling a light.""" if light.is_on: - off_params = filter_turn_off_params(call.data) + off_params = filter_turn_off_params(call.data["params"]) await light.async_turn_off(**off_params) else: await async_handle_light_on_service(light, call) diff --git a/tests/components/light/test_init.py b/tests/components/light/test_init.py index afc125e6423..72674a984fd 100644 --- a/tests/components/light/test_init.py +++ b/tests/components/light/test_init.py @@ -336,6 +336,21 @@ async def test_services(hass, mock_light_profiles): light.ATTR_TRANSITION: prof_t, } + await hass.services.async_call( + light.DOMAIN, + SERVICE_TOGGLE, + { + ATTR_ENTITY_ID: ent3.entity_id, + light.ATTR_TRANSITION: 4, + }, + blocking=True, + ) + + _, data = ent3.last_call("turn_off") + assert data == { + light.ATTR_TRANSITION: 4, + } + # Test bad data await hass.services.async_call( light.DOMAIN, SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_MATCH_ALL}, blocking=True From 21121b6e9b8bbed485a256c7cf8154f2f00266c0 Mon Sep 17 00:00:00 2001 From: ehendrix23 Date: Sat, 9 Jan 2021 07:29:48 -0700 Subject: [PATCH 5/6] Bump pymyq to 2.0.13 (#44961) --- homeassistant/components/myq/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/myq/manifest.json b/homeassistant/components/myq/manifest.json index 5f863ad7f34..653a2229296 100644 --- a/homeassistant/components/myq/manifest.json +++ b/homeassistant/components/myq/manifest.json @@ -2,7 +2,7 @@ "domain": "myq", "name": "MyQ", "documentation": "https://www.home-assistant.io/integrations/myq", - "requirements": ["pymyq==2.0.12"], + "requirements": ["pymyq==2.0.13"], "codeowners": ["@bdraco"], "config_flow": true, "homekit": { diff --git a/requirements_all.txt b/requirements_all.txt index 8e3b891c2e7..47fd57aeef4 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1536,7 +1536,7 @@ pymsteams==0.1.12 pymusiccast==0.1.6 # homeassistant.components.myq -pymyq==2.0.12 +pymyq==2.0.13 # homeassistant.components.mysensors pymysensors==0.18.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 466c072f4a9..0be9f1a1d96 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -779,7 +779,7 @@ pymodbus==2.3.0 pymonoprice==0.3 # homeassistant.components.myq -pymyq==2.0.12 +pymyq==2.0.13 # homeassistant.components.nut pynut2==2.1.2 From 9524766b07c7ab8a328c43eac18df85be6d30c71 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 9 Jan 2021 14:31:14 +0000 Subject: [PATCH 6/6] Bumped version to 2021.1.1 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 4baa7e7bc3a..13ed75196d7 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 2021 MINOR_VERSION = 1 -PATCH_VERSION = "0" +PATCH_VERSION = "1" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 7, 1)