From 4d8cf8f14a31627a50048be74cb1a65ff6334ad2 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Wed, 5 Jan 2022 12:17:54 +0100 Subject: [PATCH 01/27] Bump pychromecast to 10.2.3 (#63429) --- homeassistant/components/cast/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/cast/manifest.json b/homeassistant/components/cast/manifest.json index b084540bebb..b1a4cd0b358 100644 --- a/homeassistant/components/cast/manifest.json +++ b/homeassistant/components/cast/manifest.json @@ -3,7 +3,7 @@ "name": "Google Cast", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/cast", - "requirements": ["pychromecast==10.2.2"], + "requirements": ["pychromecast==10.2.3"], "after_dependencies": [ "cloud", "http", diff --git a/requirements_all.txt b/requirements_all.txt index 34002e25317..37b484792f2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1397,7 +1397,7 @@ pycfdns==1.2.2 pychannels==1.0.0 # homeassistant.components.cast -pychromecast==10.2.2 +pychromecast==10.2.3 # homeassistant.components.pocketcasts pycketcasts==1.0.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ccbcfd7d5e2..60471a03fcc 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -850,7 +850,7 @@ pybotvac==0.0.22 pycfdns==1.2.2 # homeassistant.components.cast -pychromecast==10.2.2 +pychromecast==10.2.3 # homeassistant.components.climacell pyclimacell==0.18.2 From 5f3e89d6386592e57d9f22749d68ddab79073651 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 5 Jan 2022 01:24:03 -1000 Subject: [PATCH 02/27] Bump flux_led to 0.27.40 to fix SK6812RGBW white level reporting (#63435) --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 6703065476e..ca28d014c96 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.27.32"], + "requirements": ["flux_led==0.27.40"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 37b484792f2..67b07d0b203 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -659,7 +659,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.32 +flux_led==0.27.40 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 60471a03fcc..617cca5cbdb 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.32 +flux_led==0.27.40 # homeassistant.components.homekit fnvhash==0.1.0 From 178b63f3b4bc21e1d347e56e9bf7d1f6e1e34fa3 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Wed, 5 Jan 2022 12:21:24 +0100 Subject: [PATCH 03/27] Fix incorrect access to entity registry in Xiaomi Miio (#63446) --- homeassistant/components/xiaomi_miio/number.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/xiaomi_miio/number.py b/homeassistant/components/xiaomi_miio/number.py index 94b3412d44a..eacc39db560 100644 --- a/homeassistant/components/xiaomi_miio/number.py +++ b/homeassistant/components/xiaomi_miio/number.py @@ -7,6 +7,7 @@ from homeassistant.components.number import NumberEntity, NumberEntityDescriptio from homeassistant.components.number.const import DOMAIN as PLATFORM_DOMAIN from homeassistant.const import DEGREE, ENTITY_CATEGORY_CONFIG, TIME_MINUTES from homeassistant.core import callback +from homeassistant.helpers import entity_registry as er from .const import ( CONF_DEVICE, @@ -251,7 +252,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): for feature, description in NUMBER_TYPES.items(): if feature == FEATURE_SET_LED_BRIGHTNESS and model != MODEL_FAN_ZA5: # Delete LED bightness entity created by mistake if it exists - entity_reg = hass.helpers.entity_registry.async_get() + entity_reg = er.async_get(hass) entity_id = entity_reg.async_get_entity_id( PLATFORM_DOMAIN, DOMAIN, f"{description.key}_{config_entry.unique_id}" ) From 2d7defbdb1b42d9feb584816570739a3bea9ad66 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 11 Dec 2021 00:12:37 -1000 Subject: [PATCH 04/27] Pickup screenlogic codeowner (#61477) - I am using this in production and already doing some work on it --- CODEOWNERS | 2 +- homeassistant/components/screenlogic/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index e1dcf3d7dc9..29a22efa45f 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -452,7 +452,7 @@ homeassistant/components/samsungtv/* @escoand @chemelli74 homeassistant/components/scene/* @home-assistant/core homeassistant/components/schluter/* @prairieapps homeassistant/components/scrape/* @fabaff -homeassistant/components/screenlogic/* @dieselrabbit +homeassistant/components/screenlogic/* @dieselrabbit @bdraco homeassistant/components/script/* @home-assistant/core homeassistant/components/search/* @home-assistant/core homeassistant/components/select/* @home-assistant/core diff --git a/homeassistant/components/screenlogic/manifest.json b/homeassistant/components/screenlogic/manifest.json index b6134216049..92f9e774183 100644 --- a/homeassistant/components/screenlogic/manifest.json +++ b/homeassistant/components/screenlogic/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/screenlogic", "requirements": ["screenlogicpy==0.5.3"], - "codeowners": ["@dieselrabbit"], + "codeowners": ["@dieselrabbit", "@bdraco"], "dhcp": [ { "hostname": "pentair: *", From 4318bec5d45a24b4e77bd65f8d0865bd3bf7bc1a Mon Sep 17 00:00:00 2001 From: Kevin Worrel <37058192+dieselrabbit@users.noreply.github.com> Date: Thu, 6 Jan 2022 00:32:55 -0800 Subject: [PATCH 05/27] Bump screenlogicpy (#63533) --- homeassistant/components/screenlogic/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/screenlogic/manifest.json b/homeassistant/components/screenlogic/manifest.json index 92f9e774183..09313dab0dd 100644 --- a/homeassistant/components/screenlogic/manifest.json +++ b/homeassistant/components/screenlogic/manifest.json @@ -3,7 +3,7 @@ "name": "Pentair ScreenLogic", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/screenlogic", - "requirements": ["screenlogicpy==0.5.3"], + "requirements": ["screenlogicpy==0.5.4"], "codeowners": ["@dieselrabbit", "@bdraco"], "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index 67b07d0b203..c00fffe27a3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2113,7 +2113,7 @@ scapy==2.4.5 schiene==0.23 # homeassistant.components.screenlogic -screenlogicpy==0.5.3 +screenlogicpy==0.5.4 # homeassistant.components.scsgate scsgate==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 617cca5cbdb..9fe99876050 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1257,7 +1257,7 @@ samsungtvws==1.6.0 scapy==2.4.5 # homeassistant.components.screenlogic -screenlogicpy==0.5.3 +screenlogicpy==0.5.4 # homeassistant.components.emulated_kasa # homeassistant.components.sense From 4ad77a758c3fdf1b1624d1e341a2a07b8952a43e Mon Sep 17 00:00:00 2001 From: jjlawren Date: Thu, 6 Jan 2022 08:26:03 -0600 Subject: [PATCH 06/27] Bump soco to 0.25.3 (#63548) --- homeassistant/components/sonos/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/sonos/manifest.json b/homeassistant/components/sonos/manifest.json index d4fce01fd78..b943013d4bc 100644 --- a/homeassistant/components/sonos/manifest.json +++ b/homeassistant/components/sonos/manifest.json @@ -3,7 +3,7 @@ "name": "Sonos", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/sonos", - "requirements": ["soco==0.25.2"], + "requirements": ["soco==0.25.3"], "dependencies": ["ssdp"], "after_dependencies": ["plex", "zeroconf"], "zeroconf": ["_sonos._tcp.local."], diff --git a/requirements_all.txt b/requirements_all.txt index c00fffe27a3..4b8742f899d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2185,7 +2185,7 @@ smhi-pkg==1.0.15 snapcast==2.1.3 # homeassistant.components.sonos -soco==0.25.2 +soco==0.25.3 # homeassistant.components.solaredge_local solaredge-local==0.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 9fe99876050..57cf7d37eaa 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1291,7 +1291,7 @@ smarthab==0.21 smhi-pkg==1.0.15 # homeassistant.components.sonos -soco==0.25.2 +soco==0.25.3 # homeassistant.components.solaredge solaredge==0.0.2 From f487f2ee31accf1c4416ff3bae8806bc0df08963 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 7 Jan 2022 08:47:14 -1000 Subject: [PATCH 07/27] Fix KeyError during call to homekit.unpair (#63627) --- homeassistant/components/homekit/__init__.py | 7 ++++++- tests/components/homekit/test_homekit.py | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/homekit/__init__.py b/homeassistant/components/homekit/__init__.py index cfa734559fc..6f2f09f3974 100644 --- a/homeassistant/components/homekit/__init__.py +++ b/homeassistant/components/homekit/__init__.py @@ -732,7 +732,12 @@ class HomeKit: """Remove all pairings for an accessory so it can be repaired.""" state = self.driver.state for client_uuid in list(state.paired_clients): - state.remove_paired_client(client_uuid) + # We need to check again since removing a single client + # can result in removing all the clients that the client + # granted access to if it was an admin, otherwise + # remove_paired_client can generate a KeyError + if client_uuid in state.paired_clients: + state.remove_paired_client(client_uuid) self.driver.async_persist() self.driver.async_update_advertisement() self._async_show_setup_message() diff --git a/tests/components/homekit/test_homekit.py b/tests/components/homekit/test_homekit.py index 0b1d2cc8535..bd637572191 100644 --- a/tests/components/homekit/test_homekit.py +++ b/tests/components/homekit/test_homekit.py @@ -683,6 +683,11 @@ async def test_homekit_unpair(hass, device_reg, mock_async_zeroconf): state = homekit.driver.state state.add_paired_client("client1", "any", b"1") + state.add_paired_client("client2", "any", b"0") + state.add_paired_client("client3", "any", b"1") + state.add_paired_client("client4", "any", b"0") + state.add_paired_client("client5", "any", b"0") + formatted_mac = device_registry.format_mac(state.mac) hk_bridge_dev = device_reg.async_get_device( {}, {(device_registry.CONNECTION_NETWORK_MAC, formatted_mac)} From 20362867ff07d00cac46800b153b8b47d61e44fd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 7 Jan 2022 15:46:39 -1000 Subject: [PATCH 08/27] Ensure selected entity is pre-selected in homekit options flow (#63628) * Ensure selected entity is pre-selected in homekit options flow - We recently adjusted the flow to exclude entities that had been deleted from breaking the UI validation. We need to include single entities in the set of all supported entities since accessory mode has no domain filter * tweak * Additional fixes * small tweak to speed up building the set * merged fixed version for test branch --- .../components/homekit/config_flow.py | 25 +++++---- tests/components/homekit/test_config_flow.py | 53 +++++++++++++++++-- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/homekit/config_flow.py b/homeassistant/components/homekit/config_flow.py index 0d8bf967c5b..34c62b31d2a 100644 --- a/homeassistant/components/homekit/config_flow.py +++ b/homeassistant/components/homekit/config_flow.py @@ -446,15 +446,25 @@ class OptionsFlowHandler(config_entries.OptionsFlow): return await self.async_step_advanced() entity_filter = self.hk_options.get(CONF_FILTER, {}) + entities = entity_filter.get(CONF_INCLUDE_ENTITIES, []) + all_supported_entities = _async_get_matching_entities( self.hass, domains=self.hk_options[CONF_DOMAINS], ) - data_schema = {} - entity_schema = vol.In - entities = entity_filter.get(CONF_INCLUDE_ENTITIES, []) - if self.hk_options[CONF_HOMEKIT_MODE] != HOMEKIT_MODE_ACCESSORY: + # Strip out entities that no longer exist to prevent error in the UI + valid_entities = [ + entity_id for entity_id in entities if entity_id in all_supported_entities + ] + if self.hk_options[CONF_HOMEKIT_MODE] == HOMEKIT_MODE_ACCESSORY: + # In accessory mode we can only have one + default_value = valid_entities[0] if valid_entities else None + entity_schema = vol.In + entities_schema_required = vol.Required + else: + # Bridge mode + entities_schema_required = vol.Optional include_exclude_mode = MODE_INCLUDE if not entities: include_exclude_mode = MODE_EXCLUDE @@ -463,13 +473,10 @@ class OptionsFlowHandler(config_entries.OptionsFlow): vol.Required(CONF_INCLUDE_EXCLUDE_MODE, default=include_exclude_mode) ] = vol.In(INCLUDE_EXCLUDE_MODES) entity_schema = cv.multi_select + default_value = valid_entities - # Strip out entities that no longer exist to prevent error in the UI - valid_entities = [ - entity_id for entity_id in entities if entity_id in all_supported_entities - ] data_schema[ - vol.Optional(CONF_ENTITIES, default=valid_entities) + entities_schema_required(CONF_ENTITIES, default=default_value) ] = entity_schema(all_supported_entities) return self.async_show_form( diff --git a/tests/components/homekit/test_config_flow.py b/tests/components/homekit/test_config_flow.py index f076d8e00ae..d190dec04b8 100644 --- a/tests/components/homekit/test_config_flow.py +++ b/tests/components/homekit/test_config_flow.py @@ -7,6 +7,8 @@ from homeassistant.config_entries import SOURCE_IGNORE, SOURCE_IMPORT from homeassistant.const import CONF_NAME, CONF_PORT from homeassistant.setup import async_setup_component +from .util import PATH_HOMEKIT, async_init_entry + from tests.common import MockConfigEntry @@ -1065,11 +1067,13 @@ async def test_options_flow_blocked_when_from_yaml(hass, mock_get_source_ip): assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY -async def test_options_flow_include_mode_basic_accessory(hass, mock_get_source_ip): +@patch(f"{PATH_HOMEKIT}.async_port_is_available", return_value=True) +async def test_options_flow_include_mode_basic_accessory( + port_mock, hass, mock_get_source_ip, hk_driver, mock_async_zeroconf +): """Test config flow options in include mode with a single accessory.""" - config_entry = _mock_config_entry_with_options_populated() - config_entry.add_to_hass(hass) + await async_init_entry(hass, config_entry) hass.states.async_set("media_player.tv", "off") hass.states.async_set("media_player.sonos", "off") @@ -1101,7 +1105,48 @@ async def test_options_flow_include_mode_basic_accessory(hass, mock_get_source_i assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM assert result2["step_id"] == "include_exclude" - assert _get_schema_default(result2["data_schema"].schema, "entities") == [] + assert _get_schema_default(result2["data_schema"].schema, "entities") is None + + result3 = await hass.config_entries.options.async_configure( + result2["flow_id"], + user_input={"entities": "media_player.tv"}, + ) + assert result3["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY + assert config_entry.options == { + "mode": "accessory", + "filter": { + "exclude_domains": [], + "exclude_entities": [], + "include_domains": [], + "include_entities": ["media_player.tv"], + }, + } + + # Now we check again to make sure the single entity is still + # preselected + + result = await hass.config_entries.options.async_init( + config_entry.entry_id, context={"show_advanced_options": False} + ) + + assert result["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result["step_id"] == "init" + assert result["data_schema"]({}) == { + "domains": ["media_player"], + "mode": "accessory", + } + + result2 = await hass.config_entries.options.async_configure( + result["flow_id"], + user_input={"domains": ["media_player"], "mode": "accessory"}, + ) + + assert result2["type"] == data_entry_flow.RESULT_TYPE_FORM + assert result2["step_id"] == "include_exclude" + assert ( + _get_schema_default(result2["data_schema"].schema, "entities") + == "media_player.tv" + ) result3 = await hass.config_entries.options.async_configure( result2["flow_id"], From 1923f86b75a4b555505163ab7e5e6915ea1c02fe Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 7 Jan 2022 11:43:42 -1000 Subject: [PATCH 09/27] Bump flux_led to 0.27.41 (#63638) --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index ca28d014c96..23b754368a0 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.27.40"], + "requirements": ["flux_led==0.27.41"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 4b8742f899d..f2c357dd7f8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -659,7 +659,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.40 +flux_led==0.27.41 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 57cf7d37eaa..2666df9ddc0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.40 +flux_led==0.27.41 # homeassistant.components.homekit fnvhash==0.1.0 From b4391fd114f18befded3ee820052b5e320081364 Mon Sep 17 00:00:00 2001 From: Leah Oswald Date: Sat, 8 Jan 2022 21:01:54 +0100 Subject: [PATCH 10/27] Add default mode 'auto' for tradfri starkvind air purifier on turn on (#63641) * add default mode 'auto' for tradfri starkvind air purifier on turn on This commits fixes the behaviour described in #60122 (purifier couldn't be started via toggle switch). It adds 'auto' as default mode/fallback for turning on the starkvind air purifier. This is now the same behaviour the original app provides. * Refactor code that set 'auto' as default value for tradfri starkvind on turn on --- homeassistant/components/tradfri/fan.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/tradfri/fan.py b/homeassistant/components/tradfri/fan.py index 845d5e6d9c3..dab39f598f2 100644 --- a/homeassistant/components/tradfri/fan.py +++ b/homeassistant/components/tradfri/fan.py @@ -142,7 +142,7 @@ class TradfriAirPurifierFan(TradfriBaseDevice, FanEntity): preset_mode: str | None = None, **kwargs: Any, ) -> None: - """Turn on the fan.""" + """Turn on the fan. Auto-mode if no argument is given.""" if not self._device_control: return @@ -150,8 +150,8 @@ class TradfriAirPurifierFan(TradfriBaseDevice, FanEntity): await self._api(self._device_control.set_mode(_from_percentage(percentage))) return - if preset_mode: - await self.async_set_preset_mode(preset_mode) + preset_mode = preset_mode or ATTR_AUTO + await self.async_set_preset_mode(preset_mode) async def async_set_percentage(self, percentage: int) -> None: """Set the speed percentage of the fan.""" From 79d789c610b9fe30d7dac0bd9b3993087d1f22dd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 7 Jan 2022 15:45:39 -1000 Subject: [PATCH 11/27] Bump flux_led to 0.27.42 (#63651) - Fixes RGB/WW/CW controller operating mode not being detected after a factory reset. - When the device is factory reset or setup for the first time the operating mode is reported as 0 and its expected the user will set it in the Magic Home app. If they skip that step Home Assistant will never be able to figure out which operating mode the device is using and the CW and WW channels will not work. - Changelog: https://github.com/Danielhiversen/flux_led/compare/0.27.41...0.27.42 --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 23b754368a0..521463a8ec9 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.27.41"], + "requirements": ["flux_led==0.27.42"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index f2c357dd7f8..98552c6df7a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -659,7 +659,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.41 +flux_led==0.27.42 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2666df9ddc0..6a392fd9b5c 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.41 +flux_led==0.27.42 # homeassistant.components.homekit fnvhash==0.1.0 From ea5b18c1ef16b64cd7916f2540692ab5de2d2edf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 19 Dec 2021 00:25:30 -0600 Subject: [PATCH 12/27] Split august motion and image capture binary sensors (#62154) --- .../components/august/binary_sensor.py | 18 +++++ homeassistant/components/august/camera.py | 3 +- homeassistant/components/august/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/august/test_binary_sensor.py | 66 ++++++++++++++++--- 6 files changed, 80 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/august/binary_sensor.py b/homeassistant/components/august/binary_sensor.py index cf34952309b..541b5e94276 100644 --- a/homeassistant/components/august/binary_sensor.py +++ b/homeassistant/components/august/binary_sensor.py @@ -61,6 +61,17 @@ def _retrieve_motion_state(data: AugustData, detail: DoorbellDetail) -> bool: return _activity_time_based_state(latest) +def _retrieve_image_capture_state(data: AugustData, detail: DoorbellDetail) -> bool: + latest = data.activity_stream.get_latest_device_activity( + detail.device_id, {ActivityType.DOORBELL_IMAGE_CAPTURE} + ) + + if latest is None: + return False + + return _activity_time_based_state(latest) + + def _retrieve_ding_state(data: AugustData, detail: DoorbellDetail) -> bool: latest = data.activity_stream.get_latest_device_activity( detail.device_id, {ActivityType.DOORBELL_DING} @@ -126,6 +137,13 @@ SENSOR_TYPES_DOORBELL: tuple[AugustBinarySensorEntityDescription, ...] = ( value_fn=_retrieve_motion_state, is_time_based=True, ), + AugustBinarySensorEntityDescription( + key="doorbell_image_capture", + name="Image Capture", + icon="mdi:file-image", + value_fn=_retrieve_image_capture_state, + is_time_based=True, + ), AugustBinarySensorEntityDescription( key="doorbell_online", name="Online", diff --git a/homeassistant/components/august/camera.py b/homeassistant/components/august/camera.py index 6f9ecf1b182..6c1f31c4b9c 100644 --- a/homeassistant/components/august/camera.py +++ b/homeassistant/components/august/camera.py @@ -63,7 +63,8 @@ class AugustCamera(AugustEntityMixin, Camera): def _update_from_data(self): """Get the latest state of the sensor.""" doorbell_activity = self._data.activity_stream.get_latest_device_activity( - self._device_id, {ActivityType.DOORBELL_MOTION} + self._device_id, + {ActivityType.DOORBELL_MOTION, ActivityType.DOORBELL_IMAGE_CAPTURE}, ) if doorbell_activity is not None: diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index fc365102926..f89be2915fb 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.1.13"], + "requirements": ["yalexs==1.1.15"], "codeowners": ["@bdraco"], "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index 98552c6df7a..e920a88c6cd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2466,7 +2466,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.4 # homeassistant.components.august -yalexs==1.1.13 +yalexs==1.1.15 # homeassistant.components.yeelight yeelight==0.7.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6a392fd9b5c..1179255c888 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1464,7 +1464,7 @@ xmltodict==0.12.0 yalesmartalarmclient==0.3.4 # homeassistant.components.august -yalexs==1.1.13 +yalexs==1.1.15 # homeassistant.components.yeelight yeelight==0.7.8 diff --git a/tests/components/august/test_binary_sensor.py b/tests/components/august/test_binary_sensor.py index 26c824e5842..e2ff4a6771a 100644 --- a/tests/components/august/test_binary_sensor.py +++ b/tests/components/august/test_binary_sensor.py @@ -1,5 +1,6 @@ """The binary_sensor tests for the august platform.""" import datetime +import time from unittest.mock import Mock, patch from yalexs.pubnub_async import AugustPubNub @@ -26,6 +27,10 @@ from tests.components.august.mocks import ( ) +def _timetoken(): + return str(time.time_ns())[:-2] + + async def test_doorsense(hass): """Test creation of a lock with doorsense and bridge.""" lock_one = await _mock_lock_from_fixture( @@ -85,6 +90,10 @@ async def test_create_doorbell(hass): "binary_sensor.k98gidt45gul_name_motion" ) assert binary_sensor_k98gidt45gul_name_motion.state == STATE_OFF + binary_sensor_k98gidt45gul_name_image_capture = hass.states.get( + "binary_sensor.k98gidt45gul_name_image_capture" + ) + assert binary_sensor_k98gidt45gul_name_image_capture.state == STATE_OFF binary_sensor_k98gidt45gul_name_online = hass.states.get( "binary_sensor.k98gidt45gul_name_online" ) @@ -97,6 +106,10 @@ async def test_create_doorbell(hass): "binary_sensor.k98gidt45gul_name_motion" ) assert binary_sensor_k98gidt45gul_name_motion.state == STATE_OFF + binary_sensor_k98gidt45gul_name_image_capture = hass.states.get( + "binary_sensor.k98gidt45gul_name_image_capture" + ) + assert binary_sensor_k98gidt45gul_name_image_capture.state == STATE_OFF async def test_create_doorbell_offline(hass): @@ -171,7 +184,7 @@ async def test_doorbell_update_via_pubnub(hass): pubnub, Mock( channel=doorbell_one.pubsub_channel, - timetoken=dt_util.utcnow().timestamp() * 10000000, + timetoken=_timetoken(), message={ "status": "imagecapture", "data": { @@ -186,10 +199,46 @@ async def test_doorbell_update_via_pubnub(hass): await hass.async_block_till_done() + binary_sensor_k98gidt45gul_name_image_capture = hass.states.get( + "binary_sensor.k98gidt45gul_name_image_capture" + ) + assert binary_sensor_k98gidt45gul_name_image_capture.state == STATE_ON + + pubnub.message( + pubnub, + Mock( + channel=doorbell_one.pubsub_channel, + timetoken=_timetoken(), + message={ + "status": "doorbell_motion_detected", + "data": { + "event": "doorbell_motion_detected", + "image": { + "height": 640, + "width": 480, + "format": "jpg", + "created_at": "2021-03-16T02:36:26.886Z", + "bytes": 14061, + "secure_url": "https://dyu7azbnaoi74.cloudfront.net/images/1f8.jpeg", + "url": "https://dyu7azbnaoi74.cloudfront.net/images/1f8.jpeg", + "etag": "09e839331c4ea59eef28081f2caa0e90", + }, + "doorbellName": "Front Door", + "callID": None, + "origin": "mars-api", + "mutableContent": True, + }, + }, + ), + ) + + await hass.async_block_till_done() + binary_sensor_k98gidt45gul_name_motion = hass.states.get( "binary_sensor.k98gidt45gul_name_motion" ) assert binary_sensor_k98gidt45gul_name_motion.state == STATE_ON + binary_sensor_k98gidt45gul_name_ding = hass.states.get( "binary_sensor.k98gidt45gul_name_ding" ) @@ -204,16 +253,16 @@ async def test_doorbell_update_via_pubnub(hass): async_fire_time_changed(hass, new_time) await hass.async_block_till_done() - binary_sensor_k98gidt45gul_name_motion = hass.states.get( - "binary_sensor.k98gidt45gul_name_motion" + binary_sensor_k98gidt45gul_name_image_capture = hass.states.get( + "binary_sensor.k98gidt45gul_name_image_capture" ) - assert binary_sensor_k98gidt45gul_name_motion.state == STATE_OFF + assert binary_sensor_k98gidt45gul_name_image_capture.state == STATE_OFF pubnub.message( pubnub, Mock( channel=doorbell_one.pubsub_channel, - timetoken=dt_util.utcnow().timestamp() * 10000000, + timetoken=_timetoken(), message={ "status": "buttonpush", }, @@ -274,7 +323,7 @@ async def test_door_sense_update_via_pubnub(hass): pubnub, Mock( channel=lock_one.pubsub_channel, - timetoken=dt_util.utcnow().timestamp() * 10000000, + timetoken=_timetoken(), message={"status": "kAugLockState_Unlocking", "doorState": "closed"}, ), ) @@ -289,11 +338,10 @@ async def test_door_sense_update_via_pubnub(hass): pubnub, Mock( channel=lock_one.pubsub_channel, - timetoken=dt_util.utcnow().timestamp() * 10000000, + timetoken=_timetoken(), message={"status": "kAugLockState_Locking", "doorState": "open"}, ), ) - await hass.async_block_till_done() binary_sensor_online_with_doorsense_name = hass.states.get( "binary_sensor.online_with_doorsense_name_open" @@ -327,7 +375,7 @@ async def test_door_sense_update_via_pubnub(hass): pubnub, Mock( channel=lock_one.pubsub_channel, - timetoken=dt_util.utcnow().timestamp() * 10000000, + timetoken=_timetoken(), message={"status": "kAugLockState_Unlocking", "doorState": "open"}, ), ) From 028169c076df93d20a4b0de003cd38f425f9c8d6 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 23 Dec 2021 10:08:38 -1000 Subject: [PATCH 13/27] Bump yalexs to 1.1.16 (#62700) --- homeassistant/components/august/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index f89be2915fb..201c9ce89aa 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.1.15"], + "requirements": ["yalexs==1.1.16"], "codeowners": ["@bdraco"], "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index e920a88c6cd..ef9292bc3a9 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2466,7 +2466,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.4 # homeassistant.components.august -yalexs==1.1.15 +yalexs==1.1.16 # homeassistant.components.yeelight yeelight==0.7.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 1179255c888..45f5d057fdd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1464,7 +1464,7 @@ xmltodict==0.12.0 yalesmartalarmclient==0.3.4 # homeassistant.components.august -yalexs==1.1.15 +yalexs==1.1.16 # homeassistant.components.yeelight yeelight==0.7.8 From 0828eb5bc5e29f034ed2cd78eb92b8de971ef07c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 9 Jan 2022 19:27:27 -1000 Subject: [PATCH 14/27] Fix august locks failing to lock/unlock (#63652) --- homeassistant/components/august/__init__.py | 18 +++++ homeassistant/components/august/lock.py | 6 ++ homeassistant/components/august/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/august/mocks.py | 4 +- tests/components/august/test_lock.py | 81 +++++++++++++++++++ 7 files changed, 110 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/august/__init__.py b/homeassistant/components/august/__init__.py index 474e69db435..ff6a4f5adb6 100644 --- a/homeassistant/components/august/__init__.py +++ b/homeassistant/components/august/__init__.py @@ -245,6 +245,15 @@ class AugustData(AugustSubscriberMixin): device_id, ) + async def async_lock_async(self, device_id): + """Lock the device but do not wait for a response since it will come via pubnub.""" + return await self._async_call_api_op_requires_bridge( + device_id, + self._api.async_lock_async, + self._august_gateway.access_token, + device_id, + ) + async def async_unlock(self, device_id): """Unlock the device.""" return await self._async_call_api_op_requires_bridge( @@ -254,6 +263,15 @@ class AugustData(AugustSubscriberMixin): device_id, ) + async def async_unlock_async(self, device_id): + """Unlock the device but do not wait for a response since it will come via pubnub.""" + return await self._async_call_api_op_requires_bridge( + device_id, + self._api.async_unlock_async, + self._august_gateway.access_token, + device_id, + ) + async def _async_call_api_op_requires_bridge( self, device_id, func, *args, **kwargs ): diff --git a/homeassistant/components/august/lock.py b/homeassistant/components/august/lock.py index 665b0036557..ea977a3c2d0 100644 --- a/homeassistant/components/august/lock.py +++ b/homeassistant/components/august/lock.py @@ -41,10 +41,16 @@ class AugustLock(AugustEntityMixin, RestoreEntity, LockEntity): async def async_lock(self, **kwargs): """Lock the device.""" + if self._data.activity_stream.pubnub.connected: + await self._data.async_lock_async(self._device_id) + return await self._call_lock_operation(self._data.async_lock) async def async_unlock(self, **kwargs): """Unlock the device.""" + if self._data.activity_stream.pubnub.connected: + await self._data.async_unlock_async(self._device_id) + return await self._call_lock_operation(self._data.async_unlock) async def _call_lock_operation(self, lock_operation): diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index 201c9ce89aa..c08f25177cc 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -2,7 +2,7 @@ "domain": "august", "name": "August", "documentation": "https://www.home-assistant.io/integrations/august", - "requirements": ["yalexs==1.1.16"], + "requirements": ["yalexs==1.1.17"], "codeowners": ["@bdraco"], "dhcp": [ { diff --git a/requirements_all.txt b/requirements_all.txt index ef9292bc3a9..7a279e1586d 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2466,7 +2466,7 @@ xs1-api-client==3.0.0 yalesmartalarmclient==0.3.4 # homeassistant.components.august -yalexs==1.1.16 +yalexs==1.1.17 # homeassistant.components.yeelight yeelight==0.7.8 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 45f5d057fdd..111e12d0b64 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1464,7 +1464,7 @@ xmltodict==0.12.0 yalesmartalarmclient==0.3.4 # homeassistant.components.august -yalexs==1.1.16 +yalexs==1.1.17 # homeassistant.components.yeelight yeelight==0.7.8 diff --git a/tests/components/august/mocks.py b/tests/components/august/mocks.py index 13d8f18d0d9..7075eb84d72 100644 --- a/tests/components/august/mocks.py +++ b/tests/components/august/mocks.py @@ -2,8 +2,6 @@ import json import os import time - -# from unittest.mock import AsyncMock from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch from yalexs.activity import ( @@ -207,6 +205,8 @@ async def _mock_setup_august_with_api_side_effects(hass, api_call_side_effects, side_effect=api_call_side_effects["unlock_return_activities"] ) + api_instance.async_unlock_async = AsyncMock() + api_instance.async_lock_async = AsyncMock() api_instance.async_get_user = AsyncMock(return_value={"UserID": "abc"}) return await _mock_setup_august(hass, api_instance, pubnub) diff --git a/tests/components/august/test_lock.py b/tests/components/august/test_lock.py index 9d1c34d917a..56f55138e36 100644 --- a/tests/components/august/test_lock.py +++ b/tests/components/august/test_lock.py @@ -154,6 +154,86 @@ async def test_one_lock_operation(hass): ) +async def test_one_lock_operation_pubnub_connected(hass): + """Test lock and unlock operations are async when pubnub is connected.""" + lock_one = await _mock_doorsense_enabled_august_lock_detail(hass) + assert lock_one.pubsub_channel == "pubsub" + + pubnub = AugustPubNub() + await _create_august_with_devices(hass, [lock_one], pubnub=pubnub) + pubnub.connected = True + + lock_online_with_doorsense_name = hass.states.get("lock.online_with_doorsense_name") + + assert lock_online_with_doorsense_name.state == STATE_LOCKED + + assert lock_online_with_doorsense_name.attributes.get("battery_level") == 92 + assert ( + lock_online_with_doorsense_name.attributes.get("friendly_name") + == "online_with_doorsense Name" + ) + + data = {ATTR_ENTITY_ID: "lock.online_with_doorsense_name"} + assert await hass.services.async_call( + LOCK_DOMAIN, SERVICE_UNLOCK, data, blocking=True + ) + await hass.async_block_till_done() + + pubnub.message( + pubnub, + Mock( + channel=lock_one.pubsub_channel, + timetoken=(dt_util.utcnow().timestamp() + 1) * 10000000, + message={ + "status": "kAugLockState_Unlocked", + }, + ), + ) + await hass.async_block_till_done() + await hass.async_block_till_done() + + lock_online_with_doorsense_name = hass.states.get("lock.online_with_doorsense_name") + assert lock_online_with_doorsense_name.state == STATE_UNLOCKED + + assert lock_online_with_doorsense_name.attributes.get("battery_level") == 92 + assert ( + lock_online_with_doorsense_name.attributes.get("friendly_name") + == "online_with_doorsense Name" + ) + + assert await hass.services.async_call( + LOCK_DOMAIN, SERVICE_LOCK, data, blocking=True + ) + await hass.async_block_till_done() + + pubnub.message( + pubnub, + Mock( + channel=lock_one.pubsub_channel, + timetoken=(dt_util.utcnow().timestamp() + 2) * 10000000, + message={ + "status": "kAugLockState_Locked", + }, + ), + ) + await hass.async_block_till_done() + await hass.async_block_till_done() + + lock_online_with_doorsense_name = hass.states.get("lock.online_with_doorsense_name") + assert lock_online_with_doorsense_name.state == STATE_LOCKED + + # No activity means it will be unavailable until the activity feed has data + entity_registry = er.async_get(hass) + lock_operator_sensor = entity_registry.async_get( + "sensor.online_with_doorsense_name_operator" + ) + assert lock_operator_sensor + assert ( + hass.states.get("sensor.online_with_doorsense_name_operator").state + == STATE_UNKNOWN + ) + + async def test_lock_jammed(hass): """Test lock gets jammed on unlock.""" @@ -273,6 +353,7 @@ async def test_lock_update_via_pubnub(hass): config_entry = await _create_august_with_devices( hass, [lock_one], activities=activities, pubnub=pubnub ) + pubnub.connected = True lock_online_with_doorsense_name = hass.states.get("lock.online_with_doorsense_name") From 70d21bf3c09ad0cc0c3ab1c0143fabfbbb2a84ed Mon Sep 17 00:00:00 2001 From: RenierM26 <66512715+RenierM26@users.noreply.github.com> Date: Sat, 8 Jan 2022 13:58:31 +0200 Subject: [PATCH 15/27] Switchbot Set initial state for switch (#63654) --- homeassistant/components/switchbot/switch.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homeassistant/components/switchbot/switch.py b/homeassistant/components/switchbot/switch.py index a09255d0392..48dbd8d8cac 100644 --- a/homeassistant/components/switchbot/switch.py +++ b/homeassistant/components/switchbot/switch.py @@ -113,6 +113,7 @@ class SwitchBotBotEntity(SwitchbotEntity, SwitchEntity, RestoreEntity): super().__init__(coordinator, idx, mac, name) self._attr_unique_id = idx self._device = device + self._attr_is_on = False async def async_added_to_hass(self) -> None: """Run when entity about to be added.""" @@ -132,6 +133,7 @@ class SwitchBotBotEntity(SwitchbotEntity, SwitchEntity, RestoreEntity): ) if self._last_run_success: self._attr_is_on = True + self.async_write_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: """Turn device off.""" @@ -143,6 +145,7 @@ class SwitchBotBotEntity(SwitchbotEntity, SwitchEntity, RestoreEntity): ) if self._last_run_success: self._attr_is_on = False + self.async_write_ha_state() @property def assumed_state(self) -> bool: From 1444a3cce0eed413878495bce6cb512946c2f194 Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Mon, 10 Jan 2022 06:26:28 +0100 Subject: [PATCH 16/27] Fix Tuya climate c_f DP not being a string (#63680) --- homeassistant/components/tuya/climate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/tuya/climate.py b/homeassistant/components/tuya/climate.py index 35e6f5814f3..a56f64a8aad 100644 --- a/homeassistant/components/tuya/climate.py +++ b/homeassistant/components/tuya/climate.py @@ -148,8 +148,9 @@ class TuyaClimateEntity(TuyaEntity, ClimateEntity): ): self._attr_temperature_unit = TEMP_CELSIUS if any( - "f" in device.status.get(dpcode, "").lower() + "f" in device.status[dpcode].lower() for dpcode in (DPCode.C_F, DPCode.TEMP_UNIT_CONVERT) + if isinstance(device.status.get(dpcode), str) ): self._attr_temperature_unit = TEMP_FAHRENHEIT From 50d690544ab63151724e324da96a61b9938348fd Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Sun, 9 Jan 2022 11:27:42 +0100 Subject: [PATCH 17/27] Fix Netgear used method version (#63686) * Netgear fix used method version * add error message * fix imports * fix black * fix error mess * rename const --- .../components/netgear/config_flow.py | 8 +++---- homeassistant/components/netgear/const.py | 24 ++++++++++++++++--- homeassistant/components/netgear/router.py | 8 +++++++ tests/components/netgear/test_config_flow.py | 4 ++-- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/homeassistant/components/netgear/config_flow.py b/homeassistant/components/netgear/config_flow.py index 29a0bc40ed6..bcf51f1966d 100644 --- a/homeassistant/components/netgear/config_flow.py +++ b/homeassistant/components/netgear/config_flow.py @@ -22,8 +22,8 @@ from .const import ( DEFAULT_CONSIDER_HOME, DEFAULT_NAME, DOMAIN, - MODELS_V2, - ORBI_PORT, + MODELS_PORT_80, + PORT_80, ) from .errors import CannotLoginException from .router import get_api @@ -141,13 +141,13 @@ class NetgearFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): self._abort_if_unique_id_configured(updates=updated_data) updated_data[CONF_PORT] = DEFAULT_PORT - for model in MODELS_V2: + for model in MODELS_PORT_80: if discovery_info.upnp.get(ssdp.ATTR_UPNP_MODEL_NUMBER, "").startswith( model ) or discovery_info.upnp.get(ssdp.ATTR_UPNP_MODEL_NAME, "").startswith( model ): - updated_data[CONF_PORT] = ORBI_PORT + updated_data[CONF_PORT] = PORT_80 self.placeholders.update(updated_data) self.discovered = True diff --git a/homeassistant/components/netgear/const.py b/homeassistant/components/netgear/const.py index cba2d7ff875..81fdf1d59e2 100644 --- a/homeassistant/components/netgear/const.py +++ b/homeassistant/components/netgear/const.py @@ -10,8 +10,8 @@ CONF_CONSIDER_HOME = "consider_home" DEFAULT_CONSIDER_HOME = timedelta(seconds=180) DEFAULT_NAME = "Netgear router" -# update method V2 models -MODELS_V2 = [ +# models using port 80 instead of 5000 +MODELS_PORT_80 = [ "Orbi", "RBK", "RBR", @@ -29,7 +29,25 @@ MODELS_V2 = [ "SXR", "SXS", ] -ORBI_PORT = 80 +PORT_80 = 80 +# update method V2 models +MODELS_V2 = [ + "Orbi", + "RBK", + "RBR", + "RBS", + "RBW", + "LBK", + "LBR", + "CBK", + "CBR", + "SRC", + "SRK", + "SRS", + "SXK", + "SXR", + "SXS", +] # Icons DEVICE_ICONS = { diff --git a/homeassistant/components/netgear/router.py b/homeassistant/components/netgear/router.py index 40e26128d8d..dbfd0439a85 100644 --- a/homeassistant/components/netgear/router.py +++ b/homeassistant/components/netgear/router.py @@ -149,6 +149,14 @@ class NetgearRouter: if self.model.startswith(model): self.method_version = 2 + if self.method_version == 2: + if not self._api.get_attached_devices_2(): + _LOGGER.error( + "Netgear Model '%s' in MODELS_V2 list, but failed to get attached devices using V2", + self.model, + ) + self.method_version = 1 + async def async_setup(self) -> None: """Set up a Netgear router.""" await self.hass.async_add_executor_job(self._setup) diff --git a/tests/components/netgear/test_config_flow.py b/tests/components/netgear/test_config_flow.py index b26dce8d936..bdb68d79ab2 100644 --- a/tests/components/netgear/test_config_flow.py +++ b/tests/components/netgear/test_config_flow.py @@ -6,7 +6,7 @@ import pytest from homeassistant import data_entry_flow from homeassistant.components import ssdp -from homeassistant.components.netgear.const import CONF_CONSIDER_HOME, DOMAIN, ORBI_PORT +from homeassistant.components.netgear.const import CONF_CONSIDER_HOME, DOMAIN, PORT_80 from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_SSDP, SOURCE_USER from homeassistant.const import ( CONF_HOST, @@ -252,7 +252,7 @@ async def test_ssdp(hass, service): assert result["result"].unique_id == SERIAL assert result["title"] == TITLE assert result["data"].get(CONF_HOST) == HOST - assert result["data"].get(CONF_PORT) == ORBI_PORT + assert result["data"].get(CONF_PORT) == PORT_80 assert result["data"].get(CONF_SSL) == SSL assert result["data"].get(CONF_USERNAME) == DEFAULT_USER assert result["data"][CONF_PASSWORD] == PASSWORD From 3e02fffdb6f0c2338a1c18e7de8b60e3bc64668d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 8 Jan 2022 18:27:31 -1000 Subject: [PATCH 18/27] Bump flux_led to 0.27.44 to fix CCT ceiling lights (#63712) - Changelog: https://github.com/Danielhiversen/flux_led/compare/0.27.42...0.27.44 - The library was incorrectly speaking the older protocol to these devices when it needed to speak the newer CCT one --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 521463a8ec9..d2c759ea81a 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.27.42"], + "requirements": ["flux_led==0.27.44"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 7a279e1586d..66ea0c3ff87 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -659,7 +659,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.42 +flux_led==0.27.44 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 111e12d0b64..5b7b6119f97 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.42 +flux_led==0.27.44 # homeassistant.components.homekit fnvhash==0.1.0 From c7257934ccec918a78f01c78e76f1f6c46977a95 Mon Sep 17 00:00:00 2001 From: RenierM26 <66512715+RenierM26@users.noreply.github.com> Date: Mon, 10 Jan 2022 08:19:04 +0200 Subject: [PATCH 19/27] Bump PySwitchbot to 0.13.2 (#63713) * Bump api * gen requirements * Bump api to 0.13.2 to remove print statement. --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 69f9eddc6cd..59415d31c1e 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.13.0"], + "requirements": ["PySwitchbot==0.13.2"], "config_flow": true, "codeowners": ["@danielhiversen", "@RenierM26"], "iot_class": "local_polling" diff --git a/requirements_all.txt b/requirements_all.txt index 66ea0c3ff87..e62fa6c4449 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -46,7 +46,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -# PySwitchbot==0.13.0 +# PySwitchbot==0.13.2 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5b7b6119f97..07977bc0324 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -24,7 +24,7 @@ PyQRCode==1.2.1 PyRMVtransport==0.3.3 # homeassistant.components.switchbot -# PySwitchbot==0.13.0 +# PySwitchbot==0.13.2 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From e2c7c7f5822a2827dfd3324c196be846bd695f19 Mon Sep 17 00:00:00 2001 From: Korn <20152583+k-korn@users.noreply.github.com> Date: Sun, 9 Jan 2022 16:29:06 +0200 Subject: [PATCH 20/27] Bump WazeRouteCalculator to 0.14 (#63718) --- homeassistant/components/waze_travel_time/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/waze_travel_time/manifest.json b/homeassistant/components/waze_travel_time/manifest.json index 832ec8a12e3..7991cbccbb4 100644 --- a/homeassistant/components/waze_travel_time/manifest.json +++ b/homeassistant/components/waze_travel_time/manifest.json @@ -2,7 +2,7 @@ "domain": "waze_travel_time", "name": "Waze Travel Time", "documentation": "https://www.home-assistant.io/integrations/waze_travel_time", - "requirements": ["WazeRouteCalculator==0.13"], + "requirements": ["WazeRouteCalculator==0.14"], "codeowners": [], "config_flow": true, "iot_class": "cloud_polling" diff --git a/requirements_all.txt b/requirements_all.txt index e62fa6c4449..52242fd5de6 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -83,7 +83,7 @@ TwitterAPI==2.7.5 WSDiscovery==2.0.0 # homeassistant.components.waze_travel_time -WazeRouteCalculator==0.13 +WazeRouteCalculator==0.14 # homeassistant.components.abode abodepy==1.2.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 07977bc0324..abc82042af3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -45,7 +45,7 @@ RtmAPI==0.7.2 WSDiscovery==2.0.0 # homeassistant.components.waze_travel_time -WazeRouteCalculator==0.13 +WazeRouteCalculator==0.14 # homeassistant.components.abode abodepy==1.2.0 From 4a3d1bcb233ccc659f62f05270b06f809136cca2 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Mon, 10 Jan 2022 00:28:36 -0500 Subject: [PATCH 21/27] Fix zwave_js device actions (#63769) --- .../components/zwave_js/device_action.py | 31 +- ...ate_radio_thermostat_ct100_plus_state.json | 123 +++++++- .../components/zwave_js/test_device_action.py | 292 +++++++++++++----- 3 files changed, 360 insertions(+), 86 deletions(-) diff --git a/homeassistant/components/zwave_js/device_action.py b/homeassistant/components/zwave_js/device_action.py index 14d64f87eb7..f819a33f1d4 100644 --- a/homeassistant/components/zwave_js/device_action.py +++ b/homeassistant/components/zwave_js/device_action.py @@ -14,7 +14,14 @@ from zwave_js_server.util.command_class.meter import get_meter_type from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_ENTITY_ID, CONF_TYPE +from homeassistant.const import ( + ATTR_DEVICE_ID, + ATTR_DOMAIN, + CONF_DEVICE_ID, + CONF_DOMAIN, + CONF_ENTITY_ID, + CONF_TYPE, +) from homeassistant.core import Context, HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry @@ -227,7 +234,22 @@ async def async_call_action_from_config( if action_type not in ACTION_TYPES: raise HomeAssistantError(f"Unhandled action type {action_type}") - service_data = {k: v for k, v in config.items() if v not in (None, "")} + # Don't include domain, subtype or any null/empty values in the service call + service_data = { + k: v + for k, v in config.items() + if k not in (ATTR_DOMAIN, CONF_SUBTYPE) and v not in (None, "") + } + + # Entity services (including refresh value which is a fake entity service) expects + # just an entity ID + if action_type in ( + SERVICE_REFRESH_VALUE, + SERVICE_SET_LOCK_USERCODE, + SERVICE_CLEAR_LOCK_USERCODE, + SERVICE_RESET_METER, + ): + service_data.pop(ATTR_DEVICE_ID) await hass.services.async_call( DOMAIN, service, service_data, blocking=True, context=context ) @@ -283,7 +305,10 @@ async def async_get_action_capabilities( "extra_fields": vol.Schema( { vol.Required(ATTR_COMMAND_CLASS): vol.In( - {cc.value: cc.name for cc in CommandClass} + { + CommandClass(cc.id).value: cc.name + for cc in sorted(node.command_classes, key=lambda cc: cc.name) # type: ignore[no-any-return] + } ), vol.Required(ATTR_PROPERTY): cv.string, vol.Optional(ATTR_PROPERTY_KEY): cv.string, diff --git a/tests/components/zwave_js/fixtures/climate_radio_thermostat_ct100_plus_state.json b/tests/components/zwave_js/fixtures/climate_radio_thermostat_ct100_plus_state.json index 34df415301e..cd5a6bd4abe 100644 --- a/tests/components/zwave_js/fixtures/climate_radio_thermostat_ct100_plus_state.json +++ b/tests/components/zwave_js/fixtures/climate_radio_thermostat_ct100_plus_state.json @@ -57,7 +57,128 @@ }, { "nodeId": 13, "index": 2 } ], - "commandClasses": [], + "commandClasses": [ + { + "id": 49, + "name": "Multilevel Sensor", + "version": 5, + "isSecure": false + }, + { + "id": 64, + "name": "Thermostat Mode", + "version": 2, + "isSecure": false + }, + { + "id": 66, + "name": "Thermostat Operating State", + "version": 2, + "isSecure": false + }, + { + "id": 67, + "name": "Thermostat Setpoint", + "version": 2, + "isSecure": false + }, + { + "id": 68, + "name": "Thermostat Fan Mode", + "version": 1, + "isSecure": false + }, + { + "id": 69, + "name": "Thermostat Fan State", + "version": 1, + "isSecure": false + }, + { + "id": 89, + "name": "Association Group Information", + "version": 1, + "isSecure": false + }, + { + "id": 90, + "name": "Device Reset Locally", + "version": 1, + "isSecure": false + }, + { + "id": 94, + "name": "Z-Wave Plus Info", + "version": 2, + "isSecure": false + }, + { + "id": 96, + "name": "Multi Channel", + "version": 4, + "isSecure": false + }, + { + "id": 112, + "name": "Configuration", + "version": 1, + "isSecure": false + }, + { + "id": 114, + "name": "Manufacturer Specific", + "version": 2, + "isSecure": false + }, + { + "id": 115, + "name": "Powerlevel", + "version": 1, + "isSecure": false + }, + { + "id": 122, + "name": "Firmware Update Meta Data", + "version": 3, + "isSecure": false + }, + { + "id": 128, + "name": "Battery", + "version": 1, + "isSecure": false + }, + { + "id": 129, + "name": "Clock", + "version": 1, + "isSecure": false + }, + { + "id": 133, + "name": "Association", + "version": 2, + "isSecure": false + }, + { + "id": 134, + "name": "Version", + "version": 2, + "isSecure": false + }, + { + "id": 135, + "name": "Indicator", + "version": 1, + "isSecure": false + }, + { + "id": 142, + "name": "Multi Channel Association", + "version": 3, + "isSecure": false + } + ], "values": [ { "commandClassName": "Manufacturer Specific", diff --git a/tests/components/zwave_js/test_device_action.py b/tests/components/zwave_js/test_device_action.py index 65bc8e4bddb..0980b414a09 100644 --- a/tests/components/zwave_js/test_device_action.py +++ b/tests/components/zwave_js/test_device_action.py @@ -1,4 +1,6 @@ """The tests for Z-Wave JS device actions.""" +from unittest.mock import patch + import pytest import voluptuous_serialize from zwave_js_server.client import Client @@ -14,7 +16,7 @@ from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import config_validation as cv, device_registry from homeassistant.setup import async_setup_component -from tests.common import async_get_device_automations, async_mock_service +from tests.common import async_get_device_automations async def test_get_actions( @@ -87,8 +89,130 @@ async def test_get_actions_meter( assert len(filtered_actions) > 0 -async def test_action(hass: HomeAssistant) -> None: - """Test for turn_on and turn_off actions.""" +async def test_actions( + hass: HomeAssistant, + client: Client, + climate_radio_thermostat_ct100_plus: Node, + integration: ConfigEntry, +) -> None: + """Test actions.""" + node = climate_radio_thermostat_ct100_plus + device_id = get_device_id(client, node) + dev_reg = device_registry.async_get(hass) + device = dev_reg.async_get_device({device_id}) + assert device + + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "trigger": { + "platform": "event", + "event_type": "test_event_refresh_value", + }, + "action": { + "domain": DOMAIN, + "type": "refresh_value", + "device_id": device.id, + "entity_id": "climate.z_wave_thermostat", + }, + }, + { + "trigger": { + "platform": "event", + "event_type": "test_event_ping", + }, + "action": { + "domain": DOMAIN, + "type": "ping", + "device_id": device.id, + }, + }, + { + "trigger": { + "platform": "event", + "event_type": "test_event_set_value", + }, + "action": { + "domain": DOMAIN, + "type": "set_value", + "device_id": device.id, + "command_class": 112, + "property": 1, + "value": 1, + }, + }, + { + "trigger": { + "platform": "event", + "event_type": "test_event_set_config_parameter", + }, + "action": { + "domain": DOMAIN, + "type": "set_config_parameter", + "device_id": device.id, + "parameter": 1, + "bitmask": None, + "subtype": "2-112-0-3 (Beeper)", + "value": 1, + }, + }, + ] + }, + ) + + with patch("zwave_js_server.model.node.Node.async_poll_value") as mock_call: + hass.bus.async_fire("test_event_refresh_value") + await hass.async_block_till_done() + mock_call.assert_called_once() + args = mock_call.call_args_list[0][0] + assert len(args) == 1 + assert args[0].value_id == "13-64-1-mode" + + with patch("zwave_js_server.model.node.Node.async_ping") as mock_call: + hass.bus.async_fire("test_event_ping") + await hass.async_block_till_done() + mock_call.assert_called_once() + args = mock_call.call_args_list[0][0] + assert len(args) == 0 + + with patch("zwave_js_server.model.node.Node.async_set_value") as mock_call: + hass.bus.async_fire("test_event_set_value") + await hass.async_block_till_done() + mock_call.assert_called_once() + args = mock_call.call_args_list[0][0] + assert len(args) == 2 + assert args[0] == "13-112-0-1" + assert args[1] == 1 + + with patch( + "homeassistant.components.zwave_js.services.async_set_config_parameter" + ) as mock_call: + hass.bus.async_fire("test_event_set_config_parameter") + await hass.async_block_till_done() + mock_call.assert_called_once() + args = mock_call.call_args_list[0][0] + assert len(args) == 3 + assert args[0].node_id == 13 + assert args[1] == 1 + assert args[2] == 1 + + +async def test_lock_actions( + hass: HomeAssistant, + client: Client, + lock_schlage_be469: Node, + integration: ConfigEntry, +) -> None: + """Test actions for locks.""" + node = lock_schlage_be469 + device_id = get_device_id(client, node) + dev_reg = device_registry.async_get(hass) + device = dev_reg.async_get_device({device_id}) + assert device + assert await async_setup_component( hass, automation.DOMAIN, @@ -102,7 +226,7 @@ async def test_action(hass: HomeAssistant) -> None: "action": { "domain": DOMAIN, "type": "clear_lock_usercode", - "device_id": "fake", + "device_id": device.id, "entity_id": "lock.touchscreen_deadbolt", "code_slot": 1, }, @@ -115,97 +239,80 @@ async def test_action(hass: HomeAssistant) -> None: "action": { "domain": DOMAIN, "type": "set_lock_usercode", - "device_id": "fake", + "device_id": device.id, "entity_id": "lock.touchscreen_deadbolt", "code_slot": 1, "usercode": "1234", }, }, - { - "trigger": { - "platform": "event", - "event_type": "test_event_refresh_value", - }, - "action": { - "domain": DOMAIN, - "type": "refresh_value", - "device_id": "fake", - "entity_id": "lock.touchscreen_deadbolt", - }, - }, - { - "trigger": { - "platform": "event", - "event_type": "test_event_ping", - }, - "action": { - "domain": DOMAIN, - "type": "ping", - "device_id": "fake", - }, - }, - { - "trigger": { - "platform": "event", - "event_type": "test_event_set_value", - }, - "action": { - "domain": DOMAIN, - "type": "set_value", - "device_id": "fake", - "command_class": 112, - "property": "test", - "value": 1, - }, - }, - { - "trigger": { - "platform": "event", - "event_type": "test_event_set_config_parameter", - }, - "action": { - "domain": DOMAIN, - "type": "set_config_parameter", - "device_id": "fake", - "parameter": 3, - "bitmask": None, - "subtype": "2-112-0-3 (Beeper)", - "value": 255, - }, - }, ] }, ) - clear_lock_usercode = async_mock_service(hass, "zwave_js", "clear_lock_usercode") - hass.bus.async_fire("test_event_clear_lock_usercode") - await hass.async_block_till_done() - assert len(clear_lock_usercode) == 1 + with patch("homeassistant.components.zwave_js.lock.clear_usercode") as mock_call: + hass.bus.async_fire("test_event_clear_lock_usercode") + await hass.async_block_till_done() + mock_call.assert_called_once() + args = mock_call.call_args_list[0][0] + assert len(args) == 2 + assert args[0].node_id == node.node_id + assert args[1] == 1 - set_lock_usercode = async_mock_service(hass, "zwave_js", "set_lock_usercode") - hass.bus.async_fire("test_event_set_lock_usercode") - await hass.async_block_till_done() - assert len(set_lock_usercode) == 1 + with patch("homeassistant.components.zwave_js.lock.set_usercode") as mock_call: + hass.bus.async_fire("test_event_set_lock_usercode") + await hass.async_block_till_done() + mock_call.assert_called_once() + args = mock_call.call_args_list[0][0] + assert len(args) == 3 + assert args[0].node_id == node.node_id + assert args[1] == 1 + assert args[2] == "1234" - refresh_value = async_mock_service(hass, "zwave_js", "refresh_value") - hass.bus.async_fire("test_event_refresh_value") - await hass.async_block_till_done() - assert len(refresh_value) == 1 - ping = async_mock_service(hass, "zwave_js", "ping") - hass.bus.async_fire("test_event_ping") - await hass.async_block_till_done() - assert len(ping) == 1 +async def test_reset_meter_action( + hass: HomeAssistant, + client: Client, + aeon_smart_switch_6: Node, + integration: ConfigEntry, +) -> None: + """Test reset_meter action.""" + node = aeon_smart_switch_6 + device_id = get_device_id(client, node) + dev_reg = device_registry.async_get(hass) + device = dev_reg.async_get_device({device_id}) + assert device - set_value = async_mock_service(hass, "zwave_js", "set_value") - hass.bus.async_fire("test_event_set_value") - await hass.async_block_till_done() - assert len(set_value) == 1 + assert await async_setup_component( + hass, + automation.DOMAIN, + { + automation.DOMAIN: [ + { + "trigger": { + "platform": "event", + "event_type": "test_event_reset_meter", + }, + "action": { + "domain": DOMAIN, + "type": "reset_meter", + "device_id": device.id, + "entity_id": "sensor.smart_switch_6_electric_consumed_kwh", + }, + }, + ] + }, + ) - set_config_parameter = async_mock_service(hass, "zwave_js", "set_config_parameter") - hass.bus.async_fire("test_event_set_config_parameter") - await hass.async_block_till_done() - assert len(set_config_parameter) == 1 + with patch( + "zwave_js_server.model.endpoint.Endpoint.async_invoke_cc_api" + ) as mock_call: + hass.bus.async_fire("test_event_reset_meter") + await hass.async_block_till_done() + mock_call.assert_called_once() + args = mock_call.call_args_list[0][0] + assert len(args) == 2 + assert args[0] == CommandClass.METER + assert args[1] == "reset" async def test_get_action_capabilities( @@ -261,7 +368,28 @@ async def test_get_action_capabilities( ) assert capabilities and "extra_fields" in capabilities - cc_options = [(cc.value, cc.name) for cc in CommandClass] + cc_options = [ + (133, "Association"), + (89, "Association Group Information"), + (128, "Battery"), + (129, "Clock"), + (112, "Configuration"), + (90, "Device Reset Locally"), + (122, "Firmware Update Meta Data"), + (135, "Indicator"), + (114, "Manufacturer Specific"), + (96, "Multi Channel"), + (142, "Multi Channel Association"), + (49, "Multilevel Sensor"), + (115, "Powerlevel"), + (68, "Thermostat Fan Mode"), + (69, "Thermostat Fan State"), + (64, "Thermostat Mode"), + (66, "Thermostat Operating State"), + (67, "Thermostat Setpoint"), + (134, "Version"), + (94, "Z-Wave Plus Info"), + ] assert voluptuous_serialize.convert( capabilities["extra_fields"], custom_serializer=cv.custom_serializer From e0ba71e6cbbeecaf148982c476e832a3ca756f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Mon, 10 Jan 2022 14:49:25 +0100 Subject: [PATCH 22/27] Add client metadata to cloud register (#63794) --- homeassistant/components/cloud/http_api.py | 18 ++++++- homeassistant/components/cloud/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/cloud/test_http_api.py | 57 +++++++++++++++++--- 6 files changed, 72 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index cd682057266..2fbb1c447ae 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -21,6 +21,7 @@ from homeassistant.components.google_assistant import helpers as google_helpers from homeassistant.components.http import HomeAssistantView from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.components.websocket_api import const as ws_const +from homeassistant.util.location import async_detect_location_info from .const import ( DOMAIN, @@ -220,8 +221,23 @@ class CloudRegisterView(HomeAssistantView): hass = request.app["hass"] cloud = hass.data[DOMAIN] + client_metadata = None + + if location_info := await async_detect_location_info( + hass.helpers.aiohttp_client.async_get_clientsession() + ): + client_metadata = { + "NC_COUNTRY_CODE": location_info.country_code, + "NC_REGION_CODE": location_info.region_code, + "NC_ZIP_CODE": location_info.zip_code, + } + async with async_timeout.timeout(REQUEST_TIMEOUT): - await cloud.auth.async_register(data["email"], data["password"]) + await cloud.auth.async_register( + data["email"], + data["password"], + client_metadata=client_metadata, + ) return self.json_message("ok") diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index 517aa887a30..0bb00cd5ced 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -2,7 +2,7 @@ "domain": "cloud", "name": "Home Assistant Cloud", "documentation": "https://www.home-assistant.io/integrations/cloud", - "requirements": ["hass-nabucasa==0.50.0"], + "requirements": ["hass-nabucasa==0.51.0"], "dependencies": ["http", "webhook"], "after_dependencies": ["google_assistant", "alexa"], "codeowners": ["@home-assistant/cloud"], diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index e90fdf9db12..4753e819ead 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -15,7 +15,7 @@ certifi>=2021.5.30 ciso8601==2.2.0 cryptography==35.0.0 emoji==1.5.0 -hass-nabucasa==0.50.0 +hass-nabucasa==0.51.0 home-assistant-frontend==20211229.0 httpx==0.21.0 ifaddr==0.1.7 diff --git a/requirements_all.txt b/requirements_all.txt index 52242fd5de6..3815daa18fd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -787,7 +787,7 @@ habitipy==0.2.0 hangups==0.4.14 # homeassistant.components.cloud -hass-nabucasa==0.50.0 +hass-nabucasa==0.51.0 # homeassistant.components.splunk hass_splunk==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index abc82042af3..c2250c84479 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -494,7 +494,7 @@ habitipy==0.2.0 hangups==0.4.14 # homeassistant.components.cloud -hass-nabucasa==0.50.0 +hass-nabucasa==0.51.0 # homeassistant.components.tasmota hatasmota==0.3.1 diff --git a/tests/components/cloud/test_http_api.py b/tests/components/cloud/test_http_api.py index 42a498528ce..ac8cd49802e 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -15,6 +15,7 @@ from homeassistant.components.alexa.entities import LightCapabilities from homeassistant.components.cloud.const import DOMAIN, RequireRelink from homeassistant.components.google_assistant.helpers import GoogleEntity from homeassistant.core import State +from homeassistant.util.location import LocationInfo from . import mock_cloud, mock_cloud_prefs @@ -203,16 +204,60 @@ async def test_logout_view_unknown_error(hass, cloud_client): assert req.status == HTTPStatus.BAD_GATEWAY -async def test_register_view(mock_cognito, cloud_client): - """Test logging out.""" - req = await cloud_client.post( - "/api/cloud/register", json={"email": "hello@bla.com", "password": "falcon42"} - ) +async def test_register_view_no_location(mock_cognito, cloud_client): + """Test register without location.""" + with patch( + "homeassistant.components.cloud.http_api.async_detect_location_info", + return_value=None, + ): + req = await cloud_client.post( + "/api/cloud/register", + json={"email": "hello@bla.com", "password": "falcon42"}, + ) assert req.status == HTTPStatus.OK assert len(mock_cognito.register.mock_calls) == 1 - result_email, result_pass = mock_cognito.register.mock_calls[0][1] + call = mock_cognito.register.mock_calls[0] + result_email, result_pass = call.args assert result_email == "hello@bla.com" assert result_pass == "falcon42" + assert call.kwargs["client_metadata"] is None + + +async def test_register_view_with_location(mock_cognito, cloud_client): + """Test register with location.""" + with patch( + "homeassistant.components.cloud.http_api.async_detect_location_info", + return_value=LocationInfo( + **{ + "country_code": "XX", + "zip_code": "12345", + "region_code": "GH", + "ip": "1.2.3.4", + "city": "Gotham", + "region_name": "Gotham", + "time_zone": "Earth/Gotham", + "currency": "XXX", + "latitude": "12.34567", + "longitude": "12.34567", + "use_metric": True, + } + ), + ): + req = await cloud_client.post( + "/api/cloud/register", + json={"email": "hello@bla.com", "password": "falcon42"}, + ) + assert req.status == HTTPStatus.OK + assert len(mock_cognito.register.mock_calls) == 1 + call = mock_cognito.register.mock_calls[0] + result_email, result_pass = call.args + assert result_email == "hello@bla.com" + assert result_pass == "falcon42" + assert call.kwargs["client_metadata"] == { + "NC_COUNTRY_CODE": "XX", + "NC_REGION_CODE": "GH", + "NC_ZIP_CODE": "12345", + } async def test_register_view_bad_data(mock_cognito, cloud_client): From 50bd5d62aa747cdc7001772b210ffc2e00088e60 Mon Sep 17 00:00:00 2001 From: Knodd Date: Mon, 10 Jan 2022 15:21:03 +0100 Subject: [PATCH 23/27] Support Tuya strip lights with correct values for saturation and brightness (#63812) Co-authored-by: Franck Nijhof --- homeassistant/components/tuya/light.py | 48 ++++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/homeassistant/components/tuya/light.py b/homeassistant/components/tuya/light.py index 0669dee86c4..c6640378de5 100644 --- a/homeassistant/components/tuya/light.py +++ b/homeassistant/components/tuya/light.py @@ -30,6 +30,28 @@ from .const import DOMAIN, TUYA_DISCOVERY_NEW, DPCode, WorkMode from .util import remap_value +@dataclass +class ColorTypeData: + """Color Type Data.""" + + h_type: IntegerTypeData + s_type: IntegerTypeData + v_type: IntegerTypeData + + +DEFAULT_COLOR_TYPE_DATA = ColorTypeData( + h_type=IntegerTypeData(min=1, scale=0, max=360, step=1), + s_type=IntegerTypeData(min=1, scale=0, max=255, step=1), + v_type=IntegerTypeData(min=1, scale=0, max=255, step=1), +) + +DEFAULT_COLOR_TYPE_DATA_V2 = ColorTypeData( + h_type=IntegerTypeData(min=1, scale=0, max=360, step=1), + s_type=IntegerTypeData(min=1, scale=0, max=1000, step=1), + v_type=IntegerTypeData(min=1, scale=0, max=1000, step=1), +) + + @dataclass class TuyaLightEntityDescription(LightEntityDescription): """Describe an Tuya light entity.""" @@ -40,6 +62,7 @@ class TuyaLightEntityDescription(LightEntityDescription): color_data: DPCode | tuple[DPCode, ...] | None = None color_mode: DPCode | None = None color_temp: DPCode | tuple[DPCode, ...] | None = None + default_color_type: ColorTypeData = DEFAULT_COLOR_TYPE_DATA LIGHTS: dict[str, tuple[TuyaLightEntityDescription, ...]] = { @@ -63,6 +86,7 @@ LIGHTS: dict[str, tuple[TuyaLightEntityDescription, ...]] = { brightness=DPCode.BRIGHT_VALUE, color_temp=DPCode.TEMP_VALUE, color_data=DPCode.COLOUR_DATA, + default_color_type=DEFAULT_COLOR_TYPE_DATA_V2, ), ), # Light @@ -242,28 +266,6 @@ LIGHTS["cz"] = LIGHTS["kg"] LIGHTS["pc"] = LIGHTS["kg"] -@dataclass -class ColorTypeData: - """Color Type Data.""" - - h_type: IntegerTypeData - s_type: IntegerTypeData - v_type: IntegerTypeData - - -DEFAULT_COLOR_TYPE_DATA = ColorTypeData( - h_type=IntegerTypeData(min=1, scale=0, max=360, step=1), - s_type=IntegerTypeData(min=1, scale=0, max=255, step=1), - v_type=IntegerTypeData(min=1, scale=0, max=255, step=1), -) - -DEFAULT_COLOR_TYPE_DATA_V2 = ColorTypeData( - h_type=IntegerTypeData(min=1, scale=0, max=360, step=1), - s_type=IntegerTypeData(min=1, scale=0, max=1000, step=1), - v_type=IntegerTypeData(min=1, scale=0, max=1000, step=1), -) - - @dataclass class ColorData: """Color Data.""" @@ -443,7 +445,7 @@ class TuyaLightEntity(TuyaEntity, LightEntity): ) else: # If no type is found, use a default one - self._color_data_type = DEFAULT_COLOR_TYPE_DATA + self._color_data_type = self.entity_description.default_color_type if self._color_data_dpcode == DPCode.COLOUR_DATA_V2 or ( self._brightness_type and self._brightness_type.max > 255 ): From f6d21a04e0711e46ea9a83913591bc1df7a9f4e1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 10 Jan 2022 13:40:45 -1000 Subject: [PATCH 24/27] Bump flux_led to 0.27.45 to fix missing controls on ZJ21410 models (#63854) --- homeassistant/components/flux_led/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index d2c759ea81a..e1815b1d145 100644 --- a/homeassistant/components/flux_led/manifest.json +++ b/homeassistant/components/flux_led/manifest.json @@ -3,7 +3,7 @@ "name": "Flux LED/MagicHome", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/flux_led", - "requirements": ["flux_led==0.27.44"], + "requirements": ["flux_led==0.27.45"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/requirements_all.txt b/requirements_all.txt index 3815daa18fd..0acd5f12565 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -659,7 +659,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.44 +flux_led==0.27.45 # homeassistant.components.homekit fnvhash==0.1.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c2250c84479..7d7c714e5a7 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -399,7 +399,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.27.44 +flux_led==0.27.45 # homeassistant.components.homekit fnvhash==0.1.0 From ab4aa5dda43da46cd1f20decb6f8ac9254c0f9da Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 10 Jan 2022 13:40:57 -1000 Subject: [PATCH 25/27] Bump aioharmony to 0.2.9 (#63858) --- homeassistant/components/harmony/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/harmony/manifest.json b/homeassistant/components/harmony/manifest.json index d1b1073ebad..94817890160 100644 --- a/homeassistant/components/harmony/manifest.json +++ b/homeassistant/components/harmony/manifest.json @@ -2,7 +2,7 @@ "domain": "harmony", "name": "Logitech Harmony Hub", "documentation": "https://www.home-assistant.io/integrations/harmony", - "requirements": ["aioharmony==0.2.8"], + "requirements": ["aioharmony==0.2.9"], "codeowners": [ "@ehendrix23", "@bramkragten", diff --git a/requirements_all.txt b/requirements_all.txt index 0acd5f12565..154d4e882d3 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -177,7 +177,7 @@ aiogithubapi==21.11.0 aioguardian==2021.11.0 # homeassistant.components.harmony -aioharmony==0.2.8 +aioharmony==0.2.9 # homeassistant.components.homekit_controller aiohomekit==0.6.4 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7d7c714e5a7..29c08f517ec 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -121,7 +121,7 @@ aioflo==2021.11.0 aioguardian==2021.11.0 # homeassistant.components.harmony -aioharmony==0.2.8 +aioharmony==0.2.9 # homeassistant.components.homekit_controller aiohomekit==0.6.4 From a4f717cb5a0b722b4b670eb3381367d60e735a7a Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 10 Jan 2022 16:13:08 -0800 Subject: [PATCH 26/27] Bump frontend to 20211229.1 (#63866) --- homeassistant/components/frontend/manifest.json | 2 +- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/frontend/manifest.json b/homeassistant/components/frontend/manifest.json index c1d833ac169..b1f44c79414 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -3,7 +3,7 @@ "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", "requirements": [ - "home-assistant-frontend==20211229.0" + "home-assistant-frontend==20211229.1" ], "dependencies": [ "api", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 4753e819ead..1152007c417 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -16,7 +16,7 @@ ciso8601==2.2.0 cryptography==35.0.0 emoji==1.5.0 hass-nabucasa==0.51.0 -home-assistant-frontend==20211229.0 +home-assistant-frontend==20211229.1 httpx==0.21.0 ifaddr==0.1.7 jinja2==3.0.3 diff --git a/requirements_all.txt b/requirements_all.txt index 154d4e882d3..41c1908f511 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -820,7 +820,7 @@ hole==0.7.0 holidays==0.11.3.1 # homeassistant.components.frontend -home-assistant-frontend==20211229.0 +home-assistant-frontend==20211229.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 29c08f517ec..8beca5013ab 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -515,7 +515,7 @@ hole==0.7.0 holidays==0.11.3.1 # homeassistant.components.frontend -home-assistant-frontend==20211229.0 +home-assistant-frontend==20211229.1 # homeassistant.components.zwave homeassistant-pyozw==0.1.10 From 25b07b07d464cf2c6965d401674ec06afe7bb48e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 10 Jan 2022 16:22:00 -0800 Subject: [PATCH 27/27] Bumped version to 2021.12.9 --- homeassistant/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index 45104f0d81b..16ee024024c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from homeassistant.backports.enum import StrEnum MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 12 -PATCH_VERSION: Final = "8" +PATCH_VERSION: Final = "9" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)