diff --git a/homeassistant/components/airvisual/__init__.py b/homeassistant/components/airvisual/__init__.py index 72b063c9394..dacc1bc1e38 100644 --- a/homeassistant/components/airvisual/__init__.py +++ b/homeassistant/components/airvisual/__init__.py @@ -105,9 +105,10 @@ def async_get_cloud_coordinators_by_api_key( ) -> list[DataUpdateCoordinator]: """Get all DataUpdateCoordinator objects related to a particular API key.""" return [ - attrs[DATA_COORDINATOR] + coordinator for entry_id, attrs in hass.data[DOMAIN].items() if (entry := hass.config_entries.async_get_entry(entry_id)) + and (coordinator := attrs.get(DATA_COORDINATOR)) and entry.data.get(CONF_API_KEY) == api_key ] diff --git a/homeassistant/components/airvisual/config_flow.py b/homeassistant/components/airvisual/config_flow.py index 636da54899f..e9ccb203eaa 100644 --- a/homeassistant/components/airvisual/config_flow.py +++ b/homeassistant/components/airvisual/config_flow.py @@ -141,6 +141,9 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): existing_entry = await self.async_set_unique_id(self._geo_id) if existing_entry: self.hass.config_entries.async_update_entry(existing_entry, data=user_input) + self.hass.async_create_task( + self.hass.config_entries.async_reload(existing_entry.entry_id) + ) return self.async_abort(reason="reauth_successful") return self.async_create_entry( @@ -237,7 +240,7 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): step_id="reauth_confirm", data_schema=API_KEY_DATA_SCHEMA ) - conf = {CONF_API_KEY: user_input[CONF_API_KEY], **self._entry_data_for_reauth} + conf = {**self._entry_data_for_reauth, CONF_API_KEY: user_input[CONF_API_KEY]} return await self._async_finish_geography( conf, self._entry_data_for_reauth[CONF_INTEGRATION_TYPE] diff --git a/homeassistant/components/flux_led/light.py b/homeassistant/components/flux_led/light.py index f1fa4ed7dbb..c632492ea81 100644 --- a/homeassistant/components/flux_led/light.py +++ b/homeassistant/components/flux_led/light.py @@ -375,13 +375,21 @@ class FluxLight(FluxEntity, CoordinatorEntity, LightEntity): async def _async_turn_on(self, **kwargs: Any) -> None: """Turn the specified or all lights on.""" + if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is None: + brightness = self.brightness + if not self.is_on: await self._device.async_turn_on() if not kwargs: return - - if (brightness := kwargs.get(ATTR_BRIGHTNESS)) is None: - brightness = self.brightness + # If the brightness was previously 0, the light + # will not turn on unless brightness is at least 1 + if not brightness: + brightness = 1 + elif not brightness: + # If the device was on and brightness was not + # set, it means it was masked by an effect + brightness = 255 # Handle switch to CCT Color Mode if ATTR_COLOR_TEMP in kwargs: diff --git a/homeassistant/components/flux_led/manifest.json b/homeassistant/components/flux_led/manifest.json index 429465a0f3c..cd37897af67 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.24.17"], + "requirements": ["flux_led==0.24.24"], "quality_scale": "platinum", "codeowners": ["@icemanch"], "iot_class": "local_push", diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index d6df32fa931..09926e5d9ac 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -257,10 +257,10 @@ class FritzBoxTools: def _update_device_info(self) -> tuple[bool, str | None]: """Retrieve latest device information from the FRITZ!Box.""" - userinterface = self.connection.call_action("UserInterface1", "GetInfo") - return userinterface.get("NewUpgradeAvailable"), userinterface.get( + version = self.connection.call_action("UserInterface1", "GetInfo").get( "NewX_AVM-DE_Version" ) + return bool(version), version def scan_devices(self, now: datetime | None = None) -> None: """Scan for new devices and return a list of found device ids.""" diff --git a/homeassistant/components/gree/manifest.json b/homeassistant/components/gree/manifest.json index 62d5bec6bb8..f4f8cf153a3 100644 --- a/homeassistant/components/gree/manifest.json +++ b/homeassistant/components/gree/manifest.json @@ -3,7 +3,7 @@ "name": "Gree Climate", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/gree", - "requirements": ["greeclimate==0.12.3"], + "requirements": ["greeclimate==0.12.5"], "codeowners": ["@cmroche"], "iot_class": "local_polling" } diff --git a/homeassistant/components/lookin/manifest.json b/homeassistant/components/lookin/manifest.json index 046b0e482a1..7260985654a 100644 --- a/homeassistant/components/lookin/manifest.json +++ b/homeassistant/components/lookin/manifest.json @@ -3,7 +3,7 @@ "name": "LOOKin", "documentation": "https://www.home-assistant.io/integrations/lookin/", "codeowners": ["@ANMalko"], - "requirements": ["aiolookin==0.0.3"], + "requirements": ["aiolookin==0.0.4"], "zeroconf": ["_lookin._tcp.local."], "config_flow": true, "iot_class": "local_push" diff --git a/homeassistant/components/met/manifest.json b/homeassistant/components/met/manifest.json index 97edf8eb67f..4ebbdd3b1e7 100644 --- a/homeassistant/components/met/manifest.json +++ b/homeassistant/components/met/manifest.json @@ -3,7 +3,7 @@ "name": "Meteorologisk institutt (Met.no)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/met", - "requirements": ["pyMetno==0.8.3"], + "requirements": ["pyMetno==0.8.4"], "codeowners": ["@danielhiversen", "@thimic"], "iot_class": "cloud_polling" } diff --git a/homeassistant/components/norway_air/air_quality.py b/homeassistant/components/norway_air/air_quality.py index 8e829355ea0..f38897d62c8 100644 --- a/homeassistant/components/norway_air/air_quality.py +++ b/homeassistant/components/norway_air/air_quality.py @@ -24,6 +24,8 @@ CONF_FORECAST = "forecast" DEFAULT_FORECAST = 0 DEFAULT_NAME = "Air quality Norway" +OVERRIDE_URL = "https://aa015h6buqvih86i1.api.met.no/weatherapi/airqualityforecast/0.1/" + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { vol.Optional(CONF_FORECAST, default=DEFAULT_FORECAST): vol.Coerce(int), @@ -72,7 +74,9 @@ class AirSensor(AirQualityEntity): def __init__(self, name, coordinates, forecast, session): """Initialize the sensor.""" self._name = name - self._api = metno.AirQualityData(coordinates, forecast, session) + self._api = metno.AirQualityData( + coordinates, forecast, session, api_url=OVERRIDE_URL + ) @property def attribution(self) -> str: diff --git a/homeassistant/components/norway_air/manifest.json b/homeassistant/components/norway_air/manifest.json index 69b2e85808b..87981f085a6 100644 --- a/homeassistant/components/norway_air/manifest.json +++ b/homeassistant/components/norway_air/manifest.json @@ -2,7 +2,7 @@ "domain": "norway_air", "name": "Om Luftkvalitet i Norge (Norway Air)", "documentation": "https://www.home-assistant.io/integrations/norway_air", - "requirements": ["pyMetno==0.8.3"], + "requirements": ["pyMetno==0.8.4"], "codeowners": [], "iot_class": "cloud_polling" } diff --git a/homeassistant/components/owntracks/translations/fr.json b/homeassistant/components/owntracks/translations/fr.json index 0e753a455e0..9120cdb8637 100644 --- a/homeassistant/components/owntracks/translations/fr.json +++ b/homeassistant/components/owntracks/translations/fr.json @@ -4,7 +4,7 @@ "single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible." }, "create_entry": { - "default": "\n\n Sous Android, ouvrez [l'application OwnTracks] ( {android_url} ), acc\u00e9dez \u00e0 Pr\u00e9f\u00e9rences - > Connexion. Modifiez les param\u00e8tres suivants: \n - Mode: HTTP priv\u00e9 \n - H\u00f4te: {webhook_url} \n - Identification: \n - Nom d'utilisateur: ` ` \n - ID de p\u00e9riph\u00e9rique: ` ` \n\n Sur iOS, ouvrez [l'application OwnTracks] ( {ios_url} ), appuyez sur l'ic\u00f4ne (i) en haut \u00e0 gauche - > param\u00e8tres. Modifiez les param\u00e8tres suivants: \n - Mode: HTTP \n - URL: {webhook_url} \n - Activer l'authentification \n - ID utilisateur: ` ` \n\n {secret} \n \n Voir [la documentation] ( {docs_url} ) pour plus d'informations." + "default": "\n\n Sous Android, ouvrez [l'application OwnTracks]({android_url}), acc\u00e9dez \u00e0 Pr\u00e9f\u00e9rences - > Connexion. Modifiez les param\u00e8tres suivants: \n - Mode: HTTP priv\u00e9 \n - H\u00f4te: {webhook_url} \n - Identification: \n - Nom d'utilisateur: `''` \n - ID de p\u00e9riph\u00e9rique: `''` \n\n Sur iOS, ouvrez [l'application OwnTracks]({ios_url}), appuyez sur l'ic\u00f4ne (i) en haut \u00e0 gauche - > param\u00e8tres. Modifiez les param\u00e8tres suivants: \n - Mode: HTTP \n - URL: {webhook_url} \n - Activer l'authentification \n - ID utilisateur: `''` \n\n {secret} \n \n Voir [la documentation]({docs_url}) pour plus d'informations." }, "step": { "user": { diff --git a/homeassistant/components/xiaomi_miio/number.py b/homeassistant/components/xiaomi_miio/number.py index 161a690a0df..87ccdd63d0f 100644 --- a/homeassistant/components/xiaomi_miio/number.py +++ b/homeassistant/components/xiaomi_miio/number.py @@ -177,7 +177,7 @@ NUMBER_TYPES = { icon="mdi:star-cog", unit_of_measurement="rpm", min_value=300, - max_value=2300, + max_value=2200, step=10, method="async_set_favorite_rpm", entity_category=ENTITY_CATEGORY_CONFIG, diff --git a/homeassistant/components/zeroconf/manifest.json b/homeassistant/components/zeroconf/manifest.json index 3f4dfb4929e..95f9407661b 100644 --- a/homeassistant/components/zeroconf/manifest.json +++ b/homeassistant/components/zeroconf/manifest.json @@ -2,7 +2,7 @@ "domain": "zeroconf", "name": "Zero-configuration networking (zeroconf)", "documentation": "https://www.home-assistant.io/integrations/zeroconf", - "requirements": ["zeroconf==0.36.11"], + "requirements": ["zeroconf==0.36.13"], "dependencies": ["network", "api"], "codeowners": ["@bdraco"], "quality_scale": "internal", diff --git a/homeassistant/components/zwave_js/fan.py b/homeassistant/components/zwave_js/fan.py index 6ee709893cb..4b4f23a85d2 100644 --- a/homeassistant/components/zwave_js/fan.py +++ b/homeassistant/components/zwave_js/fan.py @@ -103,6 +103,11 @@ class ZwaveFan(ZWaveBaseEntity, FanEntity): return None return ranged_value_to_percentage(SPEED_RANGE, self.info.primary_value.value) + @property + def percentage_step(self) -> float: + """Return the step size for percentage.""" + return 1 + @property def speed_count(self) -> int: """Return the number of speeds the fan supports.""" diff --git a/homeassistant/const.py b/homeassistant/const.py index 7b9d8e4165d..5005540dbf2 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -5,7 +5,7 @@ from typing import Final MAJOR_VERSION: Final = 2021 MINOR_VERSION: Final = 11 -PATCH_VERSION: Final = "3" +PATCH_VERSION: Final = "4" __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) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 1f29737cdf3..1b4edb91b19 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -32,7 +32,7 @@ sqlalchemy==1.4.23 voluptuous-serialize==2.4.0 voluptuous==0.12.2 yarl==1.6.3 -zeroconf==0.36.11 +zeroconf==0.36.13 pycryptodome>=3.6.6 diff --git a/requirements_all.txt b/requirements_all.txt index 9aef5a12b5b..ab04da80a84 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -207,7 +207,7 @@ aiolifx_effects==0.2.2 aiolip==1.1.6 # homeassistant.components.lookin -aiolookin==0.0.3 +aiolookin==0.0.4 # homeassistant.components.lyric aiolyric==1.0.7 @@ -652,7 +652,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.24.17 +flux_led==0.24.24 # homeassistant.components.homekit fnvhash==0.1.0 @@ -747,7 +747,7 @@ gpiozero==1.5.1 gps3==0.33.3 # homeassistant.components.gree -greeclimate==0.12.3 +greeclimate==0.12.5 # homeassistant.components.greeneye_monitor greeneye_monitor==2.1 @@ -1312,7 +1312,7 @@ pyMetEireann==2021.8.0 # homeassistant.components.met # homeassistant.components.norway_air -pyMetno==0.8.3 +pyMetno==0.8.4 # homeassistant.components.rfxtrx pyRFXtrx==0.27.0 @@ -2465,7 +2465,7 @@ youtube_dl==2021.06.06 zengge==0.2 # homeassistant.components.zeroconf -zeroconf==0.36.11 +zeroconf==0.36.13 # homeassistant.components.zha zha-quirks==0.0.63 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 26d9eba1d2d..0c693ec61df 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -137,7 +137,7 @@ aiokafka==0.6.0 aiolip==1.1.6 # homeassistant.components.lookin -aiolookin==0.0.3 +aiolookin==0.0.4 # homeassistant.components.lyric aiolyric==1.0.7 @@ -387,7 +387,7 @@ fjaraskupan==1.0.2 flipr-api==1.4.1 # homeassistant.components.flux_led -flux_led==0.24.17 +flux_led==0.24.24 # homeassistant.components.homekit fnvhash==0.1.0 @@ -455,7 +455,7 @@ google-nest-sdm==0.3.8 googlemaps==2.5.1 # homeassistant.components.gree -greeclimate==0.12.3 +greeclimate==0.12.5 # homeassistant.components.growatt_server growattServer==1.1.0 @@ -784,7 +784,7 @@ pyMetEireann==2021.8.0 # homeassistant.components.met # homeassistant.components.norway_air -pyMetno==0.8.3 +pyMetno==0.8.4 # homeassistant.components.rfxtrx pyRFXtrx==0.27.0 @@ -1430,7 +1430,7 @@ yeelight==0.7.8 youless-api==0.15 # homeassistant.components.zeroconf -zeroconf==0.36.11 +zeroconf==0.36.13 # homeassistant.components.zha zha-quirks==0.0.63 diff --git a/tests/components/airvisual/test_config_flow.py b/tests/components/airvisual/test_config_flow.py index 6125b71e303..65534f1f16c 100644 --- a/tests/components/airvisual/test_config_flow.py +++ b/tests/components/airvisual/test_config_flow.py @@ -355,16 +355,19 @@ async def test_step_reauth(hass): assert result["type"] == data_entry_flow.RESULT_TYPE_FORM assert result["step_id"] == "reauth_confirm" + new_api_key = "defgh67890" + with patch( "homeassistant.components.airvisual.async_setup_entry", return_value=True ), patch("pyairvisual.air_quality.AirQuality.nearest_city", return_value=True): result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input={CONF_API_KEY: "defgh67890"} + result["flow_id"], user_input={CONF_API_KEY: new_api_key} ) assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT assert result["reason"] == "reauth_successful" assert len(hass.config_entries.async_entries()) == 1 + assert hass.config_entries.async_entries()[0].data[CONF_API_KEY] == new_api_key async def test_step_user(hass): diff --git a/tests/components/flux_led/test_light.py b/tests/components/flux_led/test_light.py index 6f0ad5aa253..01dfff85528 100644 --- a/tests/components/flux_led/test_light.py +++ b/tests/components/flux_led/test_light.py @@ -214,11 +214,26 @@ async def test_rgb_light(hass: HomeAssistant) -> None: await async_mock_device_turn_off(hass, bulb) assert hass.states.get(entity_id).state == STATE_OFF + bulb.brightness = 0 + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_RGB_COLOR: (10, 10, 30)}, + blocking=True, + ) + # If the bulb is off and we are using existing brightness + # it has to be at least 1 or the bulb won't turn on + bulb.async_set_levels.assert_called_with(10, 10, 30, brightness=1) + bulb.async_set_levels.reset_mock() + bulb.async_turn_on.reset_mock() + await hass.services.async_call( LIGHT_DOMAIN, "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True ) bulb.async_turn_on.assert_called_once() bulb.async_turn_on.reset_mock() + await async_mock_device_turn_on(hass, bulb) + assert hass.states.get(entity_id).state == STATE_ON await hass.services.async_call( LIGHT_DOMAIN, @@ -229,6 +244,19 @@ async def test_rgb_light(hass: HomeAssistant) -> None: bulb.async_set_levels.assert_called_with(255, 0, 0, brightness=100) bulb.async_set_levels.reset_mock() + await hass.services.async_call( + LIGHT_DOMAIN, + "turn_on", + {ATTR_ENTITY_ID: entity_id, ATTR_RGB_COLOR: (10, 10, 30)}, + blocking=True, + ) + # If the bulb is on and we are using existing brightness + # and brightness was 0 it means we could not read it because + # an effect is in progress so we use 255 + bulb.async_set_levels.assert_called_with(10, 10, 30, brightness=255) + bulb.async_set_levels.reset_mock() + + bulb.brightness = 128 await hass.services.async_call( LIGHT_DOMAIN, "turn_on",