From c08ae640857eead53567621d507e5749ecc99baa Mon Sep 17 00:00:00 2001 From: Matt Zimmerman Date: Fri, 9 Apr 2021 20:36:57 -0700 Subject: [PATCH 01/17] Update python-smarttub to 0.0.23 (#48978) --- homeassistant/components/smarttub/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/smarttub/manifest.json b/homeassistant/components/smarttub/manifest.json index 2425268e05c75b..5505ba69a6d337 100644 --- a/homeassistant/components/smarttub/manifest.json +++ b/homeassistant/components/smarttub/manifest.json @@ -6,7 +6,7 @@ "dependencies": [], "codeowners": ["@mdz"], "requirements": [ - "python-smarttub==0.0.19" + "python-smarttub==0.0.23" ], "quality_scale": "platinum" } diff --git a/requirements_all.txt b/requirements_all.txt index 26c9af80aa3582..349847b9791fb6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1822,7 +1822,7 @@ python-qbittorrent==0.4.2 python-ripple-api==0.0.3 # homeassistant.components.smarttub -python-smarttub==0.0.19 +python-smarttub==0.0.23 # homeassistant.components.sochain python-sochain-api==0.0.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0094e499f2237e..a8c2c3931d78ab 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -959,7 +959,7 @@ python-nest==4.1.0 python-openzwave-mqtt[mqtt-client]==1.4.0 # homeassistant.components.smarttub -python-smarttub==0.0.19 +python-smarttub==0.0.23 # homeassistant.components.songpal python-songpal==0.12 From 82cca8fb1c640bb60c7f2c4f5790b6d466da8b64 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 1 Apr 2021 15:13:58 +0200 Subject: [PATCH 02/17] Move cast config flow tests to test_config_flow (#48362) --- tests/components/cast/test_config_flow.py | 238 +++++++++++++++++++++ tests/components/cast/test_init.py | 240 +--------------------- 2 files changed, 241 insertions(+), 237 deletions(-) create mode 100644 tests/components/cast/test_config_flow.py diff --git a/tests/components/cast/test_config_flow.py b/tests/components/cast/test_config_flow.py new file mode 100644 index 00000000000000..064406df717aac --- /dev/null +++ b/tests/components/cast/test_config_flow.py @@ -0,0 +1,238 @@ +"""Tests for the Cast config flow.""" +from unittest.mock import ANY, patch + +import pytest + +from homeassistant import config_entries, data_entry_flow +from homeassistant.components import cast + +from tests.common import MockConfigEntry + + +async def test_creating_entry_sets_up_media_player(hass): + """Test setting up Cast loads the media player.""" + with patch( + "homeassistant.components.cast.media_player.async_setup_entry", + return_value=True, + ) as mock_setup, patch( + "pychromecast.discovery.discover_chromecasts", return_value=(True, None) + ), patch( + "pychromecast.discovery.stop_discovery" + ): + result = await hass.config_entries.flow.async_init( + cast.DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + # Confirmation form + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + + await hass.async_block_till_done() + + assert len(mock_setup.mock_calls) == 1 + + +@pytest.mark.parametrize("source", ["import", "user", "zeroconf"]) +async def test_single_instance(hass, source): + """Test we only allow a single config flow.""" + MockConfigEntry(domain="cast").add_to_hass(hass) + await hass.async_block_till_done() + + result = await hass.config_entries.flow.async_init( + "cast", context={"source": source} + ) + assert result["type"] == "abort" + assert result["reason"] == "single_instance_allowed" + + +async def test_user_setup(hass): + """Test we can finish a config flow.""" + result = await hass.config_entries.flow.async_init( + "cast", context={"source": "user"} + ) + assert result["type"] == "form" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + users = await hass.auth.async_get_users() + assert len(users) == 1 + assert result["type"] == "create_entry" + assert result["result"].data == { + "ignore_cec": [], + "known_hosts": [], + "uuid": [], + "user_id": users[0].id, # Home Assistant cast user + } + + +async def test_user_setup_options(hass): + """Test we can finish a config flow.""" + result = await hass.config_entries.flow.async_init( + "cast", context={"source": "user"} + ) + assert result["type"] == "form" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"known_hosts": "192.168.0.1, , 192.168.0.2 "} + ) + + users = await hass.auth.async_get_users() + assert len(users) == 1 + assert result["type"] == "create_entry" + assert result["result"].data == { + "ignore_cec": [], + "known_hosts": ["192.168.0.1", "192.168.0.2"], + "uuid": [], + "user_id": users[0].id, # Home Assistant cast user + } + + +async def test_zeroconf_setup(hass): + """Test we can finish a config flow through zeroconf.""" + result = await hass.config_entries.flow.async_init( + "cast", context={"source": "zeroconf"} + ) + assert result["type"] == "form" + + result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + + users = await hass.auth.async_get_users() + assert len(users) == 1 + assert result["type"] == "create_entry" + assert result["result"].data == { + "ignore_cec": [], + "known_hosts": [], + "uuid": [], + "user_id": users[0].id, # Home Assistant cast user + } + + +def get_suggested(schema, key): + """Get suggested value for key in voluptuous schema.""" + for k in schema.keys(): + if k == key: + if k.description is None or "suggested_value" not in k.description: + return None + return k.description["suggested_value"] + + +@pytest.mark.parametrize( + "parameter_data", + [ + ( + "known_hosts", + ["192.168.0.10", "192.168.0.11"], + "192.168.0.10,192.168.0.11", + "192.168.0.1, , 192.168.0.2 ", + ["192.168.0.1", "192.168.0.2"], + ), + ( + "uuid", + ["bla", "blu"], + "bla,blu", + "foo, , bar ", + ["foo", "bar"], + ), + ( + "ignore_cec", + ["cast1", "cast2"], + "cast1,cast2", + "other_cast, , some_cast ", + ["other_cast", "some_cast"], + ), + ], +) +async def test_option_flow(hass, parameter_data): + """Test config flow options.""" + all_parameters = ["ignore_cec", "known_hosts", "uuid"] + parameter, initial, suggested, user_input, updated = parameter_data + + data = { + "ignore_cec": [], + "known_hosts": [], + "uuid": [], + } + data[parameter] = initial + config_entry = MockConfigEntry(domain="cast", data=data) + config_entry.add_to_hass(hass) + assert await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + # Test ignore_cec and uuid options are hidden if advanced options are disabled + result = await hass.config_entries.options.async_init(config_entry.entry_id) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "options" + data_schema = result["data_schema"].schema + assert set(data_schema) == {"known_hosts"} + + # Reconfigure ignore_cec, known_hosts, uuid + context = {"source": "user", "show_advanced_options": True} + result = await hass.config_entries.options.async_init( + config_entry.entry_id, context=context + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "options" + data_schema = result["data_schema"].schema + for other_param in all_parameters: + if other_param == parameter: + continue + assert get_suggested(data_schema, other_param) == "" + assert get_suggested(data_schema, parameter) == suggested + + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={parameter: user_input}, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["data"] is None + for other_param in all_parameters: + if other_param == parameter: + continue + assert config_entry.data[other_param] == [] + assert config_entry.data[parameter] == updated + + # Clear known_hosts + result = await hass.config_entries.options.async_init(config_entry.entry_id) + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"known_hosts": ""}, + ) + assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert result["data"] is None + assert config_entry.data == {"ignore_cec": [], "known_hosts": [], "uuid": []} + + +async def test_known_hosts(hass, castbrowser_mock, castbrowser_constructor_mock): + """Test known hosts is passed to pychromecasts.""" + result = await hass.config_entries.flow.async_init( + "cast", context={"source": "user"} + ) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], {"known_hosts": "192.168.0.1, 192.168.0.2"} + ) + assert result["type"] == "create_entry" + await hass.async_block_till_done() + config_entry = hass.config_entries.async_entries("cast")[0] + + assert castbrowser_mock.start_discovery.call_count == 1 + castbrowser_constructor_mock.assert_called_once_with( + ANY, ANY, ["192.168.0.1", "192.168.0.2"] + ) + castbrowser_mock.reset_mock() + castbrowser_constructor_mock.reset_mock() + + result = await hass.config_entries.options.async_init(config_entry.entry_id) + result = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"known_hosts": "192.168.0.11, 192.168.0.12"}, + ) + + await hass.async_block_till_done() + + castbrowser_mock.start_discovery.assert_not_called() + castbrowser_constructor_mock.assert_not_called() + castbrowser_mock.host_browser.update_hosts.assert_called_once_with( + ["192.168.0.11", "192.168.0.12"] + ) diff --git a/tests/components/cast/test_init.py b/tests/components/cast/test_init.py index 888ef2ebcd797c..178f721959f875 100644 --- a/tests/components/cast/test_init.py +++ b/tests/components/cast/test_init.py @@ -1,39 +1,9 @@ -"""Tests for the Cast config flow.""" -from unittest.mock import ANY, patch +"""Tests for the Cast integration.""" +from unittest.mock import patch -import pytest - -from homeassistant import config_entries, data_entry_flow from homeassistant.components import cast from homeassistant.setup import async_setup_component -from tests.common import MockConfigEntry - - -async def test_creating_entry_sets_up_media_player(hass): - """Test setting up Cast loads the media player.""" - with patch( - "homeassistant.components.cast.media_player.async_setup_entry", - return_value=True, - ) as mock_setup, patch( - "pychromecast.discovery.discover_chromecasts", return_value=(True, None) - ), patch( - "pychromecast.discovery.stop_discovery" - ): - result = await hass.config_entries.flow.async_init( - cast.DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - # Confirmation form - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - - result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - - await hass.async_block_till_done() - - assert len(mock_setup.mock_calls) == 1 - async def test_import(hass, caplog): """Test that specifying config will create an entry.""" @@ -67,7 +37,7 @@ async def test_import(hass, caplog): async def test_not_configuring_cast_not_creates_entry(hass): - """Test that no config will not create an entry.""" + """Test that an empty config does not create an entry.""" with patch( "homeassistant.components.cast.async_setup_entry", return_value=True ) as mock_setup: @@ -75,207 +45,3 @@ async def test_not_configuring_cast_not_creates_entry(hass): await hass.async_block_till_done() assert len(mock_setup.mock_calls) == 0 - - -@pytest.mark.parametrize("source", ["import", "user", "zeroconf"]) -async def test_single_instance(hass, source): - """Test we only allow a single config flow.""" - MockConfigEntry(domain="cast").add_to_hass(hass) - await hass.async_block_till_done() - - result = await hass.config_entries.flow.async_init( - "cast", context={"source": source} - ) - assert result["type"] == "abort" - assert result["reason"] == "single_instance_allowed" - - -async def test_user_setup(hass): - """Test we can finish a config flow.""" - result = await hass.config_entries.flow.async_init( - "cast", context={"source": "user"} - ) - assert result["type"] == "form" - - result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) - - users = await hass.auth.async_get_users() - assert len(users) == 1 - assert result["type"] == "create_entry" - assert result["result"].data == { - "ignore_cec": [], - "known_hosts": [], - "uuid": [], - "user_id": users[0].id, # Home Assistant cast user - } - - -async def test_user_setup_options(hass): - """Test we can finish a config flow.""" - result = await hass.config_entries.flow.async_init( - "cast", context={"source": "user"} - ) - assert result["type"] == "form" - - result = await hass.config_entries.flow.async_configure( - result["flow_id"], {"known_hosts": "192.168.0.1, , 192.168.0.2 "} - ) - - users = await hass.auth.async_get_users() - assert len(users) == 1 - assert result["type"] == "create_entry" - assert result["result"].data == { - "ignore_cec": [], - "known_hosts": ["192.168.0.1", "192.168.0.2"], - "uuid": [], - "user_id": users[0].id, # Home Assistant cast user - } - - -async def test_zeroconf_setup(hass): - """Test we can finish a config flow through zeroconf.""" - result = await hass.config_entries.flow.async_init( - "cast", context={"source": "zeroconf"} - ) - assert result["type"] == "form" - - result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) - - users = await hass.auth.async_get_users() - assert len(users) == 1 - assert result["type"] == "create_entry" - assert result["result"].data == { - "ignore_cec": [], - "known_hosts": [], - "uuid": [], - "user_id": users[0].id, # Home Assistant cast user - } - - -def get_suggested(schema, key): - """Get suggested value for key in voluptuous schema.""" - for k in schema.keys(): - if k == key: - if k.description is None or "suggested_value" not in k.description: - return None - return k.description["suggested_value"] - - -@pytest.mark.parametrize( - "parameter_data", - [ - ( - "known_hosts", - ["192.168.0.10", "192.168.0.11"], - "192.168.0.10,192.168.0.11", - "192.168.0.1, , 192.168.0.2 ", - ["192.168.0.1", "192.168.0.2"], - ), - ( - "uuid", - ["bla", "blu"], - "bla,blu", - "foo, , bar ", - ["foo", "bar"], - ), - ( - "ignore_cec", - ["cast1", "cast2"], - "cast1,cast2", - "other_cast, , some_cast ", - ["other_cast", "some_cast"], - ), - ], -) -async def test_option_flow(hass, parameter_data): - """Test config flow options.""" - all_parameters = ["ignore_cec", "known_hosts", "uuid"] - parameter, initial, suggested, user_input, updated = parameter_data - - data = { - "ignore_cec": [], - "known_hosts": [], - "uuid": [], - } - data[parameter] = initial - config_entry = MockConfigEntry(domain="cast", data=data) - config_entry.add_to_hass(hass) - assert await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() - - # Test ignore_cec and uuid options are hidden if advanced options are disabled - result = await hass.config_entries.options.async_init(config_entry.entry_id) - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "options" - data_schema = result["data_schema"].schema - assert set(data_schema) == {"known_hosts"} - - # Reconfigure ignore_cec, known_hosts, uuid - context = {"source": "user", "show_advanced_options": True} - result = await hass.config_entries.options.async_init( - config_entry.entry_id, context=context - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_FORM - assert result["step_id"] == "options" - data_schema = result["data_schema"].schema - for other_param in all_parameters: - if other_param == parameter: - continue - assert get_suggested(data_schema, other_param) == "" - assert get_suggested(data_schema, parameter) == suggested - - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={parameter: user_input}, - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["data"] is None - for other_param in all_parameters: - if other_param == parameter: - continue - assert config_entry.data[other_param] == [] - assert config_entry.data[parameter] == updated - - # Clear known_hosts - result = await hass.config_entries.options.async_init(config_entry.entry_id) - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={"known_hosts": ""}, - ) - assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY - assert result["data"] is None - assert config_entry.data == {"ignore_cec": [], "known_hosts": [], "uuid": []} - - -async def test_known_hosts(hass, castbrowser_mock, castbrowser_constructor_mock): - """Test known hosts is passed to pychromecasts.""" - result = await hass.config_entries.flow.async_init( - "cast", context={"source": "user"} - ) - result = await hass.config_entries.flow.async_configure( - result["flow_id"], {"known_hosts": "192.168.0.1, 192.168.0.2"} - ) - assert result["type"] == "create_entry" - await hass.async_block_till_done() - config_entry = hass.config_entries.async_entries("cast")[0] - - assert castbrowser_mock.start_discovery.call_count == 1 - castbrowser_constructor_mock.assert_called_once_with( - ANY, ANY, ["192.168.0.1", "192.168.0.2"] - ) - castbrowser_mock.reset_mock() - castbrowser_constructor_mock.reset_mock() - - result = await hass.config_entries.options.async_init(config_entry.entry_id) - result = await hass.config_entries.options.async_configure( - result["flow_id"], - user_input={"known_hosts": "192.168.0.11, 192.168.0.12"}, - ) - - await hass.async_block_till_done() - - castbrowser_mock.start_discovery.assert_not_called() - castbrowser_constructor_mock.assert_not_called() - castbrowser_mock.host_browser.update_hosts.assert_called_once_with( - ["192.168.0.11", "192.168.0.12"] - ) From b96e0e69f2df75d2f459cb1ff5cfdc7a6e8bdf49 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 10 Apr 2021 00:42:42 -1000 Subject: [PATCH 03/17] Bump nexia to 0.9.6 (#48982) - Now returns None when a humidity sensor cannot be read instead of throwing an exception --- homeassistant/components/nexia/manifest.json | 2 +- homeassistant/components/nexia/util.py | 2 ++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/nexia/manifest.json b/homeassistant/components/nexia/manifest.json index cb3493ebc55985..253400c886d152 100644 --- a/homeassistant/components/nexia/manifest.json +++ b/homeassistant/components/nexia/manifest.json @@ -1,7 +1,7 @@ { "domain": "nexia", "name": "Nexia", - "requirements": ["nexia==0.9.5"], + "requirements": ["nexia==0.9.6"], "codeowners": ["@bdraco"], "documentation": "https://www.home-assistant.io/integrations/nexia", "config_flow": true, diff --git a/homeassistant/components/nexia/util.py b/homeassistant/components/nexia/util.py index 665aa137065cb1..74272a3c7fd4f6 100644 --- a/homeassistant/components/nexia/util.py +++ b/homeassistant/components/nexia/util.py @@ -13,4 +13,6 @@ def is_invalid_auth_code(http_status_code): def percent_conv(val): """Convert an actual percentage (0.0-1.0) to 0-100 scale.""" + if val is None: + return None return round(val * 100.0, 1) diff --git a/requirements_all.txt b/requirements_all.txt index 349847b9791fb6..7cef102435bc50 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -986,7 +986,7 @@ netdisco==2.8.2 neurio==0.3.1 # homeassistant.components.nexia -nexia==0.9.5 +nexia==0.9.6 # homeassistant.components.nextcloud nextcloudmonitor==1.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a8c2c3931d78ab..3f9a34509562d8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -516,7 +516,7 @@ nessclient==0.9.15 netdisco==2.8.2 # homeassistant.components.nexia -nexia==0.9.5 +nexia==0.9.6 # homeassistant.components.notify_events notify-events==1.0.4 From d081ac8d4a3d7d90b2af07800edec1335313ad16 Mon Sep 17 00:00:00 2001 From: Aidan Timson Date: Sat, 10 Apr 2021 20:48:33 +0100 Subject: [PATCH 04/17] Set Lyric hold time to use local time instead of utc (#48994) --- homeassistant/components/lyric/climate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/lyric/climate.py b/homeassistant/components/lyric/climate.py index 0e3672f952ed8a..e57bfd0c514bd2 100644 --- a/homeassistant/components/lyric/climate.py +++ b/homeassistant/components/lyric/climate.py @@ -2,7 +2,7 @@ from __future__ import annotations import logging -from time import gmtime, strftime, time +from time import localtime, strftime, time from aiolyric.objects.device import LyricDevice from aiolyric.objects.location import LyricLocation @@ -82,7 +82,7 @@ vol.Required(ATTR_TIME_PERIOD, default="01:00:00"): vol.All( cv.time_period, cv.positive_timedelta, - lambda td: strftime("%H:%M:%S", gmtime(time() + td.total_seconds())), + lambda td: strftime("%H:%M:%S", localtime(time() + td.total_seconds())), ) } From 0bb7592fab3feddb5290b4459f58d2aa66c450aa Mon Sep 17 00:00:00 2001 From: Shay Levy Date: Tue, 13 Apr 2021 01:52:51 +0300 Subject: [PATCH 05/17] Fix Shelly brightness offset (#49007) --- homeassistant/components/shelly/light.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/shelly/light.py b/homeassistant/components/shelly/light.py index a9e137968758a9..370522415fd78a 100644 --- a/homeassistant/components/shelly/light.py +++ b/homeassistant/components/shelly/light.py @@ -118,15 +118,16 @@ def brightness(self) -> int: """Brightness of light.""" if self.mode == "color": if self.control_result: - brightness = self.control_result["gain"] + brightness_pct = self.control_result["gain"] else: - brightness = self.block.gain + brightness_pct = self.block.gain else: if self.control_result: - brightness = self.control_result["brightness"] + brightness_pct = self.control_result["brightness"] else: - brightness = self.block.brightness - return int(brightness / 100 * 255) + brightness_pct = self.block.brightness + + return round(255 * brightness_pct / 100) @property def white_value(self) -> int: @@ -188,11 +189,11 @@ async def async_turn_on(self, **kwargs) -> None: set_mode = None params = {"turn": "on"} if ATTR_BRIGHTNESS in kwargs: - tmp_brightness = int(kwargs[ATTR_BRIGHTNESS] / 255 * 100) + brightness_pct = int(100 * (kwargs[ATTR_BRIGHTNESS] + 1) / 255) if hasattr(self.block, "gain"): - params["gain"] = tmp_brightness + params["gain"] = brightness_pct if hasattr(self.block, "brightness"): - params["brightness"] = tmp_brightness + params["brightness"] = brightness_pct if ATTR_COLOR_TEMP in kwargs: color_temp = color_temperature_mired_to_kelvin(kwargs[ATTR_COLOR_TEMP]) color_temp = min(self._max_kelvin, max(self._min_kelvin, color_temp)) From 0a5a5ff053ca6b4da7dd8638544f0d1f73c390b7 Mon Sep 17 00:00:00 2001 From: Joakim Plate Date: Sun, 11 Apr 2021 08:42:32 +0200 Subject: [PATCH 06/17] Bump ha-philipsjs to 2.7.0 (#49008) This has some improvements to not consider the TV off due to some exceptions that is related to API being buggy rather than off. --- homeassistant/components/philips_js/__init__.py | 6 +++++- homeassistant/components/philips_js/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/philips_js/__init__.py b/homeassistant/components/philips_js/__init__.py index 7be5efeaf2fd7b..b585451cdb0762 100644 --- a/homeassistant/components/philips_js/__init__.py +++ b/homeassistant/components/philips_js/__init__.py @@ -134,8 +134,12 @@ def _update_listeners(): async def _notify_task(self): while self.api.on and self.api.notify_change_supported: - if await self.api.notifyChange(130): + res = await self.api.notifyChange(130) + if res: self.async_set_updated_data(None) + elif res is None: + LOGGER.debug("Aborting notify due to unexpected return") + break @callback def _async_notify_stop(self): diff --git a/homeassistant/components/philips_js/manifest.json b/homeassistant/components/philips_js/manifest.json index ad591ad330bdc4..36e01d8f3c8a6c 100644 --- a/homeassistant/components/philips_js/manifest.json +++ b/homeassistant/components/philips_js/manifest.json @@ -3,7 +3,7 @@ "name": "Philips TV", "documentation": "https://www.home-assistant.io/integrations/philips_js", "requirements": [ - "ha-philipsjs==2.3.2" + "ha-philipsjs==2.7.0" ], "codeowners": [ "@elupus" diff --git a/requirements_all.txt b/requirements_all.txt index 7cef102435bc50..8507178958e608 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -721,7 +721,7 @@ guppy3==3.1.0 ha-ffmpeg==3.0.2 # homeassistant.components.philips_js -ha-philipsjs==2.3.2 +ha-philipsjs==2.7.0 # homeassistant.components.habitica habitipy==0.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 3f9a34509562d8..a95c1201d0c68f 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -382,7 +382,7 @@ guppy3==3.1.0 ha-ffmpeg==3.0.2 # homeassistant.components.philips_js -ha-philipsjs==2.3.2 +ha-philipsjs==2.7.0 # homeassistant.components.habitica habitipy==0.2.0 From e3b3d136d808dc0f1d9075f23a89037ec0efefa0 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Sat, 10 Apr 2021 22:03:44 +0200 Subject: [PATCH 07/17] Fix use search instead of match to filter logs (#49017) --- homeassistant/components/logger/__init__.py | 2 +- tests/components/logger/test_init.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/logger/__init__.py b/homeassistant/components/logger/__init__.py index fb2920fb6e25df..c7660f2a3f009a 100644 --- a/homeassistant/components/logger/__init__.py +++ b/homeassistant/components/logger/__init__.py @@ -114,7 +114,7 @@ def _add_log_filter(logger, patterns): """Add a Filter to the logger based on a regexp of the filter_str.""" def filter_func(logrecord): - return not any(p.match(logrecord.getMessage()) for p in patterns) + return not any(p.search(logrecord.getMessage()) for p in patterns) logger.addFilter(filter_func) diff --git a/tests/components/logger/test_init.py b/tests/components/logger/test_init.py index d2b0e8931b6908..6435ef95394217 100644 --- a/tests/components/logger/test_init.py +++ b/tests/components/logger/test_init.py @@ -42,6 +42,7 @@ async def test_log_filtering(hass, caplog): "doesntmatchanything", ".*shouldfilterall.*", "^filterthis:.*", + "in the middle", ], "test.other_filter": [".*otherfilterer"], }, @@ -62,6 +63,7 @@ def msg_test(logger, result, message, *args): filter_logger, False, "this line containing shouldfilterall should be filtered" ) msg_test(filter_logger, True, "this line should not be filtered filterthis:") + msg_test(filter_logger, False, "this in the middle should be filtered") msg_test(filter_logger, False, "filterthis: should be filtered") msg_test(filter_logger, False, "format string shouldfilter%s", "all") msg_test(filter_logger, True, "format string shouldfilter%s", "not") From 36e08e770b04aa9f5aab2fc54788fdb6ee63cba6 Mon Sep 17 00:00:00 2001 From: Chris Talkington Date: Sun, 11 Apr 2021 12:35:42 -0500 Subject: [PATCH 08/17] Resolve potential roku setup memory leaks (#49025) * resolve potential roku setup memory leaks * Update __init__.py --- homeassistant/components/roku/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/roku/__init__.py b/homeassistant/components/roku/__init__.py index 4a349265459562..f8294c878dde23 100644 --- a/homeassistant/components/roku/__init__.py +++ b/homeassistant/components/roku/__init__.py @@ -47,10 +47,12 @@ async def async_setup(hass: HomeAssistantType, config: dict) -> bool: async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry) -> bool: """Set up Roku from a config entry.""" - coordinator = RokuDataUpdateCoordinator(hass, host=entry.data[CONF_HOST]) - await coordinator.async_config_entry_first_refresh() + coordinator = hass.data[DOMAIN].get(entry.entry_id) + if not coordinator: + coordinator = RokuDataUpdateCoordinator(hass, host=entry.data[CONF_HOST]) + hass.data[DOMAIN][entry.entry_id] = coordinator - hass.data[DOMAIN][entry.entry_id] = coordinator + await coordinator.async_config_entry_first_refresh() for platform in PLATFORMS: hass.async_create_task( From 0d00e49dfca623330281ed13c7ab4f6f7531c0b4 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Apr 2021 06:42:46 -1000 Subject: [PATCH 09/17] Bump aiohomekit to 0.2.61 (#49044) --- homeassistant/components/homekit_controller/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 9580a7ee50d191..d4e7eb83ee3bc7 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/homekit_controller", "requirements": [ - "aiohomekit==0.2.60" + "aiohomekit==0.2.61" ], "zeroconf": [ "_hap._tcp.local." diff --git a/requirements_all.txt b/requirements_all.txt index 8507178958e608..b7443061ee8397 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -172,7 +172,7 @@ aioguardian==1.0.4 aioharmony==0.2.7 # homeassistant.components.homekit_controller -aiohomekit==0.2.60 +aiohomekit==0.2.61 # homeassistant.components.emulated_hue # homeassistant.components.http diff --git a/requirements_test_all.txt b/requirements_test_all.txt index a95c1201d0c68f..8e710e3800a32e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -106,7 +106,7 @@ aioguardian==1.0.4 aioharmony==0.2.7 # homeassistant.components.homekit_controller -aiohomekit==0.2.60 +aiohomekit==0.2.61 # homeassistant.components.emulated_hue # homeassistant.components.http From e685b1a1e34611da0fb860a0e7977bb793894bce Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 12 Apr 2021 01:53:44 +0200 Subject: [PATCH 10/17] Fix cast options flow overwriting data (#49051) --- homeassistant/components/cast/config_flow.py | 2 +- tests/components/cast/test_config_flow.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/cast/config_flow.py b/homeassistant/components/cast/config_flow.py index 464283e07f30cc..86d85588967e30 100644 --- a/homeassistant/components/cast/config_flow.py +++ b/homeassistant/components/cast/config_flow.py @@ -133,7 +133,7 @@ async def async_step_options(self, user_input=None): ) if not bad_cec and not bad_hosts and not bad_uuid: - updated_config = {} + updated_config = dict(current_config) updated_config[CONF_IGNORE_CEC] = ignore_cec updated_config[CONF_KNOWN_HOSTS] = known_hosts updated_config[CONF_UUID] = wanted_uuid diff --git a/tests/components/cast/test_config_flow.py b/tests/components/cast/test_config_flow.py index 064406df717aac..1febd9d880324d 100644 --- a/tests/components/cast/test_config_flow.py +++ b/tests/components/cast/test_config_flow.py @@ -166,6 +166,7 @@ async def test_option_flow(hass, parameter_data): assert result["step_id"] == "options" data_schema = result["data_schema"].schema assert set(data_schema) == {"known_hosts"} + orig_data = dict(config_entry.data) # Reconfigure ignore_cec, known_hosts, uuid context = {"source": "user", "show_advanced_options": True} @@ -201,7 +202,12 @@ async def test_option_flow(hass, parameter_data): ) assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["data"] is None - assert config_entry.data == {"ignore_cec": [], "known_hosts": [], "uuid": []} + assert config_entry.data == { + **orig_data, + "ignore_cec": [], + "known_hosts": [], + "uuid": [], + } async def test_known_hosts(hass, castbrowser_mock, castbrowser_constructor_mock): From e0131f726f7196e2173fcdd22a641d8e3146ab15 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Mon, 12 Apr 2021 18:32:12 +0200 Subject: [PATCH 11/17] Quote media_source paths (#49054) * Quote path in async_sign_path * Address review comments, add tests * Update tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 Co-authored-by: Paulus Schoutsen --- homeassistant/components/cast/media_player.py | 3 ++- homeassistant/components/http/auth.py | 10 ++++++++-- homeassistant/components/media_source/__init__.py | 3 ++- tests/components/media_source/test_init.py | 12 +++++++----- tests/components/media_source/test_local_source.py | 3 +++ tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 | 1 + 6 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 diff --git a/homeassistant/components/cast/media_player.py b/homeassistant/components/cast/media_player.py index 016d5162d2380d..afd6065cb98788 100644 --- a/homeassistant/components/cast/media_player.py +++ b/homeassistant/components/cast/media_player.py @@ -7,6 +7,7 @@ import functools as ft import json import logging +from urllib.parse import quote import pychromecast from pychromecast.controllers.homeassistant import HomeAssistantController @@ -472,7 +473,7 @@ async def async_play_media(self, media_type, media_id, **kwargs): media_id = async_sign_path( self.hass, refresh_token.id, - media_id, + quote(media_id), timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME), ) diff --git a/homeassistant/components/http/auth.py b/homeassistant/components/http/auth.py index 3267c9cc70e744..382758194832f9 100644 --- a/homeassistant/components/http/auth.py +++ b/homeassistant/components/http/auth.py @@ -1,6 +1,7 @@ """Authentication for HTTP component.""" import logging import secrets +from urllib.parse import unquote from aiohttp import hdrs from aiohttp.web import middleware @@ -30,11 +31,16 @@ def async_sign_path(hass, refresh_token_id, path, expiration): now = dt_util.utcnow() encoded = jwt.encode( - {"iss": refresh_token_id, "path": path, "iat": now, "exp": now + expiration}, + { + "iss": refresh_token_id, + "path": unquote(path), + "iat": now, + "exp": now + expiration, + }, secret, algorithm="HS256", ) - return f"{path}?{SIGN_QUERY_PARAM}=" f"{encoded.decode()}" + return f"{path}?{SIGN_QUERY_PARAM}={encoded.decode()}" @callback diff --git a/homeassistant/components/media_source/__init__.py b/homeassistant/components/media_source/__init__.py index 0ef5d460580ff5..5b027a99bf9ce3 100644 --- a/homeassistant/components/media_source/__init__.py +++ b/homeassistant/components/media_source/__init__.py @@ -2,6 +2,7 @@ from __future__ import annotations from datetime import timedelta +from urllib.parse import quote import voluptuous as vol @@ -123,7 +124,7 @@ async def websocket_resolve_media(hass, connection, msg): url = async_sign_path( hass, connection.refresh_token_id, - url, + quote(url), timedelta(seconds=msg["expires"]), ) diff --git a/tests/components/media_source/test_init.py b/tests/components/media_source/test_init.py index 0dda9f67fbe36f..d8ee73ebc2f117 100644 --- a/tests/components/media_source/test_init.py +++ b/tests/components/media_source/test_init.py @@ -1,5 +1,6 @@ """Test Media Source initialization.""" from unittest.mock import patch +from urllib.parse import quote import pytest @@ -45,7 +46,7 @@ async def test_async_browse_media(hass): media = await media_source.async_browse_media(hass, "") assert isinstance(media, media_source.models.BrowseMediaSource) assert media.title == "media/" - assert len(media.children) == 1 + assert len(media.children) == 2 # Test invalid media content with pytest.raises(ValueError): @@ -133,14 +134,15 @@ async def test_websocket_browse_media(hass, hass_ws_client): assert msg["error"]["message"] == "test" -async def test_websocket_resolve_media(hass, hass_ws_client): +@pytest.mark.parametrize("filename", ["test.mp3", "Epic Sax Guy 10 Hours.mp4"]) +async def test_websocket_resolve_media(hass, hass_ws_client, filename): """Test browse media websocket.""" assert await async_setup_component(hass, const.DOMAIN, {}) await hass.async_block_till_done() client = await hass_ws_client(hass) - media = media_source.models.PlayMedia("/media/local/test.mp3", "audio/mpeg") + media = media_source.models.PlayMedia(f"/media/local/{filename}", "audio/mpeg") with patch( "homeassistant.components.media_source.async_resolve_media", @@ -150,7 +152,7 @@ async def test_websocket_resolve_media(hass, hass_ws_client): { "id": 1, "type": "media_source/resolve_media", - "media_content_id": f"{const.URI_SCHEME}{const.DOMAIN}/local/test.mp3", + "media_content_id": f"{const.URI_SCHEME}{const.DOMAIN}/local/{filename}", } ) @@ -158,7 +160,7 @@ async def test_websocket_resolve_media(hass, hass_ws_client): assert msg["success"] assert msg["id"] == 1 - assert msg["result"]["url"].startswith(media.url) + assert msg["result"]["url"].startswith(quote(media.url)) assert msg["result"]["mime_type"] == media.mime_type with patch( diff --git a/tests/components/media_source/test_local_source.py b/tests/components/media_source/test_local_source.py index e3e2a3f1617d61..aff4f92be02289 100644 --- a/tests/components/media_source/test_local_source.py +++ b/tests/components/media_source/test_local_source.py @@ -95,5 +95,8 @@ async def test_media_view(hass, hass_client): resp = await client.get("/media/local/test.mp3") assert resp.status == 200 + resp = await client.get("/media/local/Epic Sax Guy 10 Hours.mp4") + assert resp.status == 200 + resp = await client.get("/media/recordings/test.mp3") assert resp.status == 200 diff --git a/tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 b/tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 new file mode 100644 index 00000000000000..23bd6ccc564867 --- /dev/null +++ b/tests/testing_config/media/Epic Sax Guy 10 Hours.mp4 @@ -0,0 +1 @@ +I play the sax From 21b55515063fa288d2453e1c9b0f16ee16acf04f Mon Sep 17 00:00:00 2001 From: Jan Bouwhuis Date: Mon, 12 Apr 2021 01:53:07 +0200 Subject: [PATCH 12/17] mqtt fan percentage to speed_range and received speed_state fix (#49060) * percentage to speed_range and get speed state fix * Update homeassistant/components/mqtt/fan.py * Update homeassistant/components/mqtt/fan.py * Update homeassistant/components/mqtt/fan.py * Update homeassistant/components/mqtt/fan.py Co-authored-by: J. Nick Koston --- homeassistant/components/mqtt/fan.py | 16 ++++++++-------- tests/components/mqtt/test_fan.py | 20 ++++++++++++++++++-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/mqtt/fan.py b/homeassistant/components/mqtt/fan.py index 6009b941c5c3d8..24c4c805dfda26 100644 --- a/homeassistant/components/mqtt/fan.py +++ b/homeassistant/components/mqtt/fan.py @@ -1,6 +1,7 @@ """Support for MQTT fans.""" import functools import logging +import math import voluptuous as vol @@ -441,13 +442,12 @@ def speed_received(msg): ) return - if not self._feature_percentage: - if speed in self._legacy_speeds_list_no_off: - self._percentage = ordered_list_item_to_percentage( - self._legacy_speeds_list_no_off, speed - ) - elif speed == SPEED_OFF: - self._percentage = 0 + if speed in self._legacy_speeds_list_no_off: + self._percentage = ordered_list_item_to_percentage( + self._legacy_speeds_list_no_off, speed + ) + elif speed == SPEED_OFF: + self._percentage = 0 self.async_write_ha_state() @@ -592,7 +592,7 @@ async def async_set_percentage(self, percentage: int) -> None: This method is a coroutine. """ - percentage_payload = int( + percentage_payload = math.ceil( percentage_to_ranged_value(self._speed_range, percentage) ) mqtt_payload = self._command_templates[ATTR_PERCENTAGE](percentage_payload) diff --git a/tests/components/mqtt/test_fan.py b/tests/components/mqtt/test_fan.py index 5caec9b7473f79..bfa1f387bcd592 100644 --- a/tests/components/mqtt/test_fan.py +++ b/tests/components/mqtt/test_fan.py @@ -618,7 +618,7 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(hass, mqtt_mock) "percentage_state_topic": "percentage-state-topic1", "percentage_command_topic": "percentage-command-topic1", "speed_range_min": 1, - "speed_range_max": 100, + "speed_range_max": 3, }, { "platform": "mqtt", @@ -651,9 +651,25 @@ async def test_sending_mqtt_commands_with_alternate_speed_range(hass, mqtt_mock) state = hass.states.get("fan.test1") assert state.attributes.get(ATTR_ASSUMED_STATE) + await common.async_set_percentage(hass, "fan.test1", 33) + mqtt_mock.async_publish.assert_called_once_with( + "percentage-command-topic1", "1", 0, False + ) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get("fan.test1") + assert state.attributes.get(ATTR_ASSUMED_STATE) + + await common.async_set_percentage(hass, "fan.test1", 66) + mqtt_mock.async_publish.assert_called_once_with( + "percentage-command-topic1", "2", 0, False + ) + mqtt_mock.async_publish.reset_mock() + state = hass.states.get("fan.test1") + assert state.attributes.get(ATTR_ASSUMED_STATE) + await common.async_set_percentage(hass, "fan.test1", 100) mqtt_mock.async_publish.assert_called_once_with( - "percentage-command-topic1", "100", 0, False + "percentage-command-topic1", "3", 0, False ) mqtt_mock.async_publish.reset_mock() state = hass.states.get("fan.test1") From 4eb794ae84df9b073697c6c9ca55a86a164f618b Mon Sep 17 00:00:00 2001 From: Kevin Worrel <37058192+dieselrabbit@users.noreply.github.com> Date: Sun, 11 Apr 2021 15:12:59 -0700 Subject: [PATCH 13/17] Catch unknown equipment values (#49073) * Catch unknown equipment values * Catch unknown equipment values * Remove warning spam. --- homeassistant/components/screenlogic/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/screenlogic/__init__.py b/homeassistant/components/screenlogic/__init__.py index 11f71cfd337112..c5c082cd509d05 100644 --- a/homeassistant/components/screenlogic/__init__.py +++ b/homeassistant/components/screenlogic/__init__.py @@ -195,9 +195,15 @@ def device_info(self): """Return device information for the controller.""" controller_type = self.config_data["controller_type"] hardware_type = self.config_data["hardware_type"] + try: + equipment_model = EQUIPMENT.CONTROLLER_HARDWARE[controller_type][ + hardware_type + ] + except KeyError: + equipment_model = f"Unknown Model C:{controller_type} H:{hardware_type}" return { "connections": {(dr.CONNECTION_NETWORK_MAC, self.mac)}, "name": self.gateway_name, "manufacturer": "Pentair", - "model": EQUIPMENT.CONTROLLER_HARDWARE[controller_type][hardware_type], + "model": equipment_model, } From bf282687323b68dd56034f9bae3e79d0de3cbc42 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 11 Apr 2021 10:36:26 -1000 Subject: [PATCH 14/17] Downgrade logger message about homekit id missing (#49079) This can happen if the TXT record is received after the PTR record and should not generate a warning since it will get processed later --- homeassistant/components/homekit_controller/config_flow.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py index fcf83918fda071..4a3deee4d117db 100644 --- a/homeassistant/components/homekit_controller/config_flow.py +++ b/homeassistant/components/homekit_controller/config_flow.py @@ -209,8 +209,11 @@ async def async_step_zeroconf(self, discovery_info): } if "id" not in properties: - _LOGGER.warning( - "HomeKit device %s: id not exposed, in violation of spec", properties + # This can happen if the TXT record is received after the PTR record + # we will wait for the next update in this case + _LOGGER.debug( + "HomeKit device %s: id not exposed; TXT record may have not yet been received", + properties, ) return self.async_abort(reason="invalid_properties") From b5650bdd526461684fd530281a6e24dafae5b123 Mon Sep 17 00:00:00 2001 From: Unai Date: Tue, 13 Apr 2021 00:14:29 +0200 Subject: [PATCH 15/17] Upgrade maxcube-api to 0.4.2 (#49106) Upgrade to maxcube-api 0.4.2 to fix pending issues in HA 2021.4.x: - Interpret correctly S command error responses (https://github.com/home-assistant/core/issues/49075) - Support application timezone configuration (https://github.com/home-assistant/core/issues/49076) --- homeassistant/components/maxcube/__init__.py | 3 ++- homeassistant/components/maxcube/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/maxcube/conftest.py | 3 ++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/maxcube/__init__.py b/homeassistant/components/maxcube/__init__.py index e38f08809a749a..4d610dfc04ffd8 100644 --- a/homeassistant/components/maxcube/__init__.py +++ b/homeassistant/components/maxcube/__init__.py @@ -10,6 +10,7 @@ from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SCAN_INTERVAL import homeassistant.helpers.config_validation as cv from homeassistant.helpers.discovery import load_platform +from homeassistant.util.dt import now _LOGGER = logging.getLogger(__name__) @@ -59,7 +60,7 @@ def setup(hass, config): scan_interval = gateway[CONF_SCAN_INTERVAL].total_seconds() try: - cube = MaxCube(host, port) + cube = MaxCube(host, port, now=now) hass.data[DATA_KEY][host] = MaxCubeHandle(cube, scan_interval) except timeout as ex: _LOGGER.error("Unable to connect to Max!Cube gateway: %s", str(ex)) diff --git a/homeassistant/components/maxcube/manifest.json b/homeassistant/components/maxcube/manifest.json index ddc21bd2358f0c..75b5a5fcb6df4b 100644 --- a/homeassistant/components/maxcube/manifest.json +++ b/homeassistant/components/maxcube/manifest.json @@ -2,6 +2,6 @@ "domain": "maxcube", "name": "eQ-3 MAX!", "documentation": "https://www.home-assistant.io/integrations/maxcube", - "requirements": ["maxcube-api==0.4.1"], + "requirements": ["maxcube-api==0.4.2"], "codeowners": [] } diff --git a/requirements_all.txt b/requirements_all.txt index b7443061ee8397..4831b310f8ea8a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -916,7 +916,7 @@ magicseaweed==1.0.3 matrix-client==0.3.2 # homeassistant.components.maxcube -maxcube-api==0.4.1 +maxcube-api==0.4.2 # homeassistant.components.mythicbeastsdns mbddns==0.1.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 8e710e3800a32e..33ef9a95aa82ce 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -476,7 +476,7 @@ logi_circle==0.2.2 luftdaten==0.6.4 # homeassistant.components.maxcube -maxcube-api==0.4.1 +maxcube-api==0.4.2 # homeassistant.components.mythicbeastsdns mbddns==0.1.2 diff --git a/tests/components/maxcube/conftest.py b/tests/components/maxcube/conftest.py index 6b283cf87c025f..b36072190c4b75 100644 --- a/tests/components/maxcube/conftest.py +++ b/tests/components/maxcube/conftest.py @@ -10,6 +10,7 @@ from homeassistant.components.maxcube import DOMAIN from homeassistant.setup import async_setup_component +from homeassistant.util.dt import now @pytest.fixture @@ -105,5 +106,5 @@ async def cube(hass, hass_config, room, thermostat, wallthermostat, windowshutte assert await async_setup_component(hass, DOMAIN, hass_config) await hass.async_block_till_done() gateway = hass_config[DOMAIN]["gateways"][0] - mock.assert_called_with(gateway["host"], gateway.get("port", 62910)) + mock.assert_called_with(gateway["host"], gateway.get("port", 62910), now=now) return cube From 346ae78a8e168ebe948f265874c06f70ef489a1f Mon Sep 17 00:00:00 2001 From: jjlawren Date: Mon, 12 Apr 2021 11:29:45 -0500 Subject: [PATCH 16/17] Check all endpoints for zwave_js.climate hvac_action (#49115) --- homeassistant/components/zwave_js/climate.py | 1 + tests/components/zwave_js/test_climate.py | 2 ++ tests/components/zwave_js/test_services.py | 2 +- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zwave_js/climate.py b/homeassistant/components/zwave_js/climate.py index c64a5ef788fc9b..41ea873c5f8922 100644 --- a/homeassistant/components/zwave_js/climate.py +++ b/homeassistant/components/zwave_js/climate.py @@ -150,6 +150,7 @@ def __init__( THERMOSTAT_OPERATING_STATE_PROPERTY, command_class=CommandClass.THERMOSTAT_OPERATING_STATE, add_to_watched_value_ids=True, + check_all_endpoints=True, ) self._current_temp = self.get_zwave_value( THERMOSTAT_CURRENT_TEMP_PROPERTY, diff --git a/tests/components/zwave_js/test_climate.py b/tests/components/zwave_js/test_climate.py index 83a607f3add39c..2084e771546f69 100644 --- a/tests/components/zwave_js/test_climate.py +++ b/tests/components/zwave_js/test_climate.py @@ -12,6 +12,7 @@ ATTR_PRESET_MODE, ATTR_TARGET_TEMP_HIGH, ATTR_TARGET_TEMP_LOW, + CURRENT_HVAC_COOL, CURRENT_HVAC_IDLE, DOMAIN as CLIMATE_DOMAIN, HVAC_MODE_COOL, @@ -351,6 +352,7 @@ async def test_thermostat_different_endpoints( assert state.attributes[ATTR_CURRENT_TEMPERATURE] == 22.8 assert state.attributes[ATTR_FAN_MODE] == "Auto low" assert state.attributes[ATTR_FAN_STATE] == "Idle / off" + assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL async def test_setpoint_thermostat(hass, client, climate_danfoss_lc_13, integration): diff --git a/tests/components/zwave_js/test_services.py b/tests/components/zwave_js/test_services.py index 7bdba7894d2f50..956361d39536a7 100644 --- a/tests/components/zwave_js/test_services.py +++ b/tests/components/zwave_js/test_services.py @@ -528,7 +528,7 @@ async def test_poll_value( }, blocking=True, ) - assert len(client.async_send_command.call_args_list) == 7 + assert len(client.async_send_command.call_args_list) == 8 # Test polling against an invalid entity raises ValueError with pytest.raises(ValueError): From e5281051a3012bf1033ca1ed5b1865ec054e8994 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Tue, 13 Apr 2021 00:10:42 +0000 Subject: [PATCH 17/17] Bumped version to 2021.4.4 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 9e6fe036d5cb52..875e60e2202201 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -1,7 +1,7 @@ """Constants used by Home Assistant components.""" MAJOR_VERSION = 2021 MINOR_VERSION = 4 -PATCH_VERSION = "3" +PATCH_VERSION = "4" __short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__ = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER = (3, 8, 0)