From 8a7e2922c26cb5a972967fcbf06ec72ca6f68fbe Mon Sep 17 00:00:00 2001 From: shbatm Date: Thu, 2 Feb 2023 13:27:30 -0600 Subject: [PATCH 01/14] Support ISY994 Z-Wave motorized blinds as cover (#87102) --- homeassistant/components/isy994/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/isy994/const.py b/homeassistant/components/isy994/const.py index 211939f8eb6..95e68c5fa11 100644 --- a/homeassistant/components/isy994/const.py +++ b/homeassistant/components/isy994/const.py @@ -255,7 +255,7 @@ NODE_FILTERS: dict[Platform, dict[str, list[str]]] = { FILTER_STATES: ["open", "closed", "closing", "opening", "stopped"], FILTER_NODE_DEF_ID: ["DimmerMotorSwitch_ADV"], FILTER_INSTEON_TYPE: [TYPE_CATEGORY_COVER], - FILTER_ZWAVE_CAT: [], + FILTER_ZWAVE_CAT: ["106", "107"], }, Platform.LIGHT: { FILTER_UOM: ["51"], From b24d0a86eeb095b8972de4c6ee1f1b01641475a5 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 2 Feb 2023 04:56:18 +0100 Subject: [PATCH 02/14] Bump reolink_aio to 0.3.1 (#87118) --- homeassistant/components/reolink/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/reolink/manifest.json b/homeassistant/components/reolink/manifest.json index 88e2e3b7730..3516241feec 100644 --- a/homeassistant/components/reolink/manifest.json +++ b/homeassistant/components/reolink/manifest.json @@ -3,7 +3,7 @@ "name": "Reolink IP NVR/camera", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/reolink", - "requirements": ["reolink-aio==0.3.0"], + "requirements": ["reolink-aio==0.3.1"], "dependencies": ["webhook"], "codeowners": ["@starkillerOG"], "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 6ccb4d457e5..7c44269a097 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2227,7 +2227,7 @@ regenmaschine==2022.11.0 renault-api==0.1.11 # homeassistant.components.reolink -reolink-aio==0.3.0 +reolink-aio==0.3.1 # homeassistant.components.python_script restrictedpython==6.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b91f5d6d144..e1a21fcf16d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1572,7 +1572,7 @@ regenmaschine==2022.11.0 renault-api==0.1.11 # homeassistant.components.reolink -reolink-aio==0.3.0 +reolink-aio==0.3.1 # homeassistant.components.python_script restrictedpython==6.0 From 264b6d4f777f0e4b3974e4cf2f55d4fa39631a70 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 2 Feb 2023 11:24:06 +0100 Subject: [PATCH 03/14] Bump reolink-aio to 0.3.2 (#87121) --- homeassistant/components/reolink/__init__.py | 18 ++----- homeassistant/components/reolink/host.py | 50 ++++++++++--------- .../components/reolink/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 5 files changed, 34 insertions(+), 40 deletions(-) diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index c20aff637ec..56974d9ed83 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -9,14 +9,7 @@ import logging from aiohttp import ClientConnectorError import async_timeout -from reolink_aio.exceptions import ( - ApiError, - InvalidContentTypeError, - LoginError, - NoDataError, - ReolinkError, - UnexpectedDataError, -) +from reolink_aio.exceptions import CredentialsInvalidError, ReolinkError from homeassistant.config_entries import ConfigEntry from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform @@ -48,17 +41,14 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b try: await host.async_init() - except UserNotAdmin as err: + except (UserNotAdmin, CredentialsInvalidError) as err: + await host.stop() raise ConfigEntryAuthFailed(err) from err except ( ClientConnectorError, asyncio.TimeoutError, - ApiError, - InvalidContentTypeError, - LoginError, - NoDataError, ReolinkException, - UnexpectedDataError, + ReolinkError, ) as err: await host.stop() raise ConfigEntryNotReady( diff --git a/homeassistant/components/reolink/host.py b/homeassistant/components/reolink/host.py index 3e0731ac8ce..582a52ae1ba 100644 --- a/homeassistant/components/reolink/host.py +++ b/homeassistant/components/reolink/host.py @@ -9,7 +9,7 @@ from typing import Any import aiohttp from aiohttp.web import Request from reolink_aio.api import Host -from reolink_aio.exceptions import ReolinkError +from reolink_aio.exceptions import ReolinkError, SubscriptionError from homeassistant.components import webhook from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT, CONF_USERNAME @@ -76,7 +76,6 @@ class ReolinkHost: raise ReolinkSetupException("Could not get mac address") if not self._api.is_admin: - await self.stop() raise UserNotAdmin( f"User '{self._api.username}' has authorization level " f"'{self._api.user_level}', only admin users can change camera settings" @@ -182,22 +181,19 @@ class ReolinkHost: ) return - if await self._api.subscribe(self._webhook_url): - _LOGGER.debug( - "Host %s: subscribed successfully to webhook %s", - self._api.host, - self._webhook_url, - ) - else: - raise ReolinkWebhookException( - f"Host {self._api.host}: webhook subscription failed" - ) + await self._api.subscribe(self._webhook_url) + + _LOGGER.debug( + "Host %s: subscribed successfully to webhook %s", + self._api.host, + self._webhook_url, + ) async def renew(self) -> None: """Renew the subscription of motion events (lease time is 15 minutes).""" try: await self._renew() - except ReolinkWebhookException as err: + except SubscriptionError as err: if not self._lost_subscription: self._lost_subscription = True _LOGGER.error( @@ -220,25 +216,33 @@ class ReolinkHost: return timer = self._api.renewtimer + _LOGGER.debug( + "Host %s:%s should renew subscription in: %i seconds", + self._api.host, + self._api.port, + timer, + ) if timer > SUBSCRIPTION_RENEW_THRESHOLD: return if timer > 0: - if await self._api.renew(): + try: + await self._api.renew() + except SubscriptionError as err: + _LOGGER.debug( + "Host %s: error renewing Reolink subscription, " + "trying to subscribe again: %s", + self._api.host, + err, + ) + else: _LOGGER.debug( "Host %s successfully renewed Reolink subscription", self._api.host ) return - _LOGGER.debug( - "Host %s: error renewing Reolink subscription, " - "trying to subscribe again", - self._api.host, - ) - if not await self._api.subscribe(self._webhook_url): - raise ReolinkWebhookException( - f"Host {self._api.host}: webhook re-subscription failed" - ) + await self._api.subscribe(self._webhook_url) + _LOGGER.debug( "Host %s: Reolink re-subscription successful after it was expired", self._api.host, diff --git a/homeassistant/components/reolink/manifest.json b/homeassistant/components/reolink/manifest.json index 3516241feec..ab944314054 100644 --- a/homeassistant/components/reolink/manifest.json +++ b/homeassistant/components/reolink/manifest.json @@ -3,7 +3,7 @@ "name": "Reolink IP NVR/camera", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/reolink", - "requirements": ["reolink-aio==0.3.1"], + "requirements": ["reolink-aio==0.3.2"], "dependencies": ["webhook"], "codeowners": ["@starkillerOG"], "iot_class": "local_polling", diff --git a/requirements_all.txt b/requirements_all.txt index 7c44269a097..f8abf9d975b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2227,7 +2227,7 @@ regenmaschine==2022.11.0 renault-api==0.1.11 # homeassistant.components.reolink -reolink-aio==0.3.1 +reolink-aio==0.3.2 # homeassistant.components.python_script restrictedpython==6.0 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index e1a21fcf16d..18071e51a33 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1572,7 +1572,7 @@ regenmaschine==2022.11.0 renault-api==0.1.11 # homeassistant.components.reolink -reolink-aio==0.3.1 +reolink-aio==0.3.2 # homeassistant.components.python_script restrictedpython==6.0 From ef8029ebbff4b1cb1c5eed20ab02e9ebf8f1cbf6 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 2 Feb 2023 10:38:21 +0100 Subject: [PATCH 04/14] Fix invalid state class in renault (#87135) --- homeassistant/components/renault/sensor.py | 2 +- tests/components/renault/const.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/homeassistant/components/renault/sensor.py b/homeassistant/components/renault/sensor.py index 679fbbd370f..52538d1e873 100644 --- a/homeassistant/components/renault/sensor.py +++ b/homeassistant/components/renault/sensor.py @@ -257,7 +257,7 @@ SENSOR_TYPES: tuple[RenaultSensorEntityDescription[Any], ...] = ( device_class=SensorDeviceClass.ENERGY, name="Battery available energy", native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, - state_class=SensorStateClass.MEASUREMENT, + state_class=SensorStateClass.TOTAL, ), RenaultSensorEntityDescription( key="battery_temperature", diff --git a/tests/components/renault/const.py b/tests/components/renault/const.py index 5db47c5d589..b15b00b825f 100644 --- a/tests/components/renault/const.py +++ b/tests/components/renault/const.py @@ -139,7 +139,7 @@ MOCK_VEHICLES = { ATTR_DEVICE_CLASS: SensorDeviceClass.ENERGY, ATTR_ENTITY_ID: "sensor.reg_number_battery_available_energy", ATTR_STATE: "31", - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, + ATTR_STATE_CLASS: SensorStateClass.TOTAL, ATTR_UNIQUE_ID: "vf1aaaaa555777999_battery_available_energy", ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, }, @@ -368,7 +368,7 @@ MOCK_VEHICLES = { ATTR_DEVICE_CLASS: SensorDeviceClass.ENERGY, ATTR_ENTITY_ID: "sensor.reg_number_battery_available_energy", ATTR_STATE: "0", - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, + ATTR_STATE_CLASS: SensorStateClass.TOTAL, ATTR_UNIQUE_ID: "vf1aaaaa555777999_battery_available_energy", ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, }, @@ -597,7 +597,7 @@ MOCK_VEHICLES = { ATTR_DEVICE_CLASS: SensorDeviceClass.ENERGY, ATTR_ENTITY_ID: "sensor.reg_number_battery_available_energy", ATTR_STATE: "31", - ATTR_STATE_CLASS: SensorStateClass.MEASUREMENT, + ATTR_STATE_CLASS: SensorStateClass.TOTAL, ATTR_UNIQUE_ID: "vf1aaaaa555777123_battery_available_energy", ATTR_UNIT_OF_MEASUREMENT: UnitOfEnergy.KILO_WATT_HOUR, }, From 517e89ab3caf13d8e0e69cf995081d0d505c6569 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Thu, 2 Feb 2023 09:44:26 +0100 Subject: [PATCH 05/14] Add missing converters to recorder statistics (#87137) --- homeassistant/components/recorder/statistics.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/homeassistant/components/recorder/statistics.py b/homeassistant/components/recorder/statistics.py index 4a39abd9f63..7a5fba6ea60 100644 --- a/homeassistant/components/recorder/statistics.py +++ b/homeassistant/components/recorder/statistics.py @@ -36,8 +36,12 @@ from homeassistant.helpers.typing import UNDEFINED, UndefinedType from homeassistant.util import dt as dt_util from homeassistant.util.unit_conversion import ( BaseUnitConverter, + DataRateConverter, DistanceConverter, + ElectricCurrentConverter, + ElectricPotentialConverter, EnergyConverter, + InformationConverter, MassConverter, PowerConverter, PressureConverter, @@ -128,8 +132,15 @@ QUERY_STATISTIC_META = [ STATISTIC_UNIT_TO_UNIT_CONVERTER: dict[str | None, type[BaseUnitConverter]] = { + **{unit: DataRateConverter for unit in DataRateConverter.VALID_UNITS}, **{unit: DistanceConverter for unit in DistanceConverter.VALID_UNITS}, + **{unit: ElectricCurrentConverter for unit in ElectricCurrentConverter.VALID_UNITS}, + **{ + unit: ElectricPotentialConverter + for unit in ElectricPotentialConverter.VALID_UNITS + }, **{unit: EnergyConverter for unit in EnergyConverter.VALID_UNITS}, + **{unit: InformationConverter for unit in InformationConverter.VALID_UNITS}, **{unit: MassConverter for unit in MassConverter.VALID_UNITS}, **{unit: PowerConverter for unit in PowerConverter.VALID_UNITS}, **{unit: PressureConverter for unit in PressureConverter.VALID_UNITS}, From 56a583e6acb8b5268b1c763dfcf1824030e09257 Mon Sep 17 00:00:00 2001 From: Dmitry Vlasov Date: Thu, 2 Feb 2023 12:36:38 +0300 Subject: [PATCH 06/14] Add missing supported features to Z-Wave.Me siren (#87141) --- homeassistant/components/zwave_me/siren.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/zwave_me/siren.py b/homeassistant/components/zwave_me/siren.py index f59face65d4..c6757f61ad7 100644 --- a/homeassistant/components/zwave_me/siren.py +++ b/homeassistant/components/zwave_me/siren.py @@ -1,7 +1,7 @@ """Representation of a sirenBinary.""" from typing import Any -from homeassistant.components.siren import SirenEntity +from homeassistant.components.siren import SirenEntity, SirenEntityFeature from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -41,6 +41,13 @@ async def async_setup_entry( class ZWaveMeSiren(ZWaveMeEntity, SirenEntity): """Representation of a ZWaveMe siren.""" + def __init__(self, controller, device): + """Initialize the device.""" + super().__init__(controller, device) + self._attr_supported_features = ( + SirenEntityFeature.TURN_ON | SirenEntityFeature.TURN_OFF + ) + @property def is_on(self) -> bool: """Return the state of the siren.""" From a58e4e0f8844dc092924c67a70d104b56f2fa309 Mon Sep 17 00:00:00 2001 From: starkillerOG Date: Thu, 2 Feb 2023 21:21:41 +0100 Subject: [PATCH 07/14] Reolink unsubscribe webhook when first refresh fails (#87147) * catch ValueError on webhook async_register * add ONVIF to webhook_id * Unsubscribe webhook when ConfigEntryNotReady for async_config_entry_first_refresh * Revert catching ValueError --- homeassistant/components/reolink/__init__.py | 6 +++++- homeassistant/components/reolink/host.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/reolink/__init__.py b/homeassistant/components/reolink/__init__.py index 56974d9ed83..f15ce90427c 100644 --- a/homeassistant/components/reolink/__init__.py +++ b/homeassistant/components/reolink/__init__.py @@ -80,7 +80,11 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b update_interval=timedelta(seconds=DEVICE_UPDATE_INTERVAL), ) # Fetch initial data so we have data when entities subscribe - await coordinator_device_config_update.async_config_entry_first_refresh() + try: + await coordinator_device_config_update.async_config_entry_first_refresh() + except ConfigEntryNotReady as err: + await host.stop() + raise err hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = ReolinkData( host=host, diff --git a/homeassistant/components/reolink/host.py b/homeassistant/components/reolink/host.py index 582a52ae1ba..e44623e1a1e 100644 --- a/homeassistant/components/reolink/host.py +++ b/homeassistant/components/reolink/host.py @@ -250,7 +250,7 @@ class ReolinkHost: async def register_webhook(self) -> None: """Register the webhook for motion events.""" - self.webhook_id = f"{DOMAIN}_{self.unique_id.replace(':', '')}" + self.webhook_id = f"{DOMAIN}_{self.unique_id.replace(':', '')}_ONVIF" event_id = self.webhook_id webhook.async_register( From be77d7daa5d0702073e27fcf781c03317b12e190 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 2 Feb 2023 13:52:55 -0600 Subject: [PATCH 08/14] Fix statistics graphs not loading with data_rate, electric_current, voltage, information, and unitless units (#87202) * Add missing converts to recorder/statistics_during_period API This was resulting in the stats graphs not loading on the frontend * its in two places --- .../components/recorder/websocket_api.py | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/homeassistant/components/recorder/websocket_api.py b/homeassistant/components/recorder/websocket_api.py index c63e5bc43ca..8733d675069 100644 --- a/homeassistant/components/recorder/websocket_api.py +++ b/homeassistant/components/recorder/websocket_api.py @@ -15,13 +15,18 @@ from homeassistant.helpers import config_validation as cv from homeassistant.helpers.json import JSON_DUMP from homeassistant.util import dt as dt_util from homeassistant.util.unit_conversion import ( + DataRateConverter, DistanceConverter, + ElectricCurrentConverter, + ElectricPotentialConverter, EnergyConverter, + InformationConverter, MassConverter, PowerConverter, PressureConverter, SpeedConverter, TemperatureConverter, + UnitlessRatioConverter, VolumeConverter, ) @@ -47,6 +52,24 @@ from .util import ( _LOGGER: logging.Logger = logging.getLogger(__package__) +UNIT_SCHEMA = vol.Schema( + { + vol.Optional("data_rate"): vol.In(DataRateConverter.VALID_UNITS), + vol.Optional("distance"): vol.In(DistanceConverter.VALID_UNITS), + vol.Optional("electric_current"): vol.In(ElectricCurrentConverter.VALID_UNITS), + vol.Optional("voltage"): vol.In(ElectricPotentialConverter.VALID_UNITS), + vol.Optional("energy"): vol.In(EnergyConverter.VALID_UNITS), + vol.Optional("information"): vol.In(InformationConverter.VALID_UNITS), + vol.Optional("mass"): vol.In(MassConverter.VALID_UNITS), + vol.Optional("power"): vol.In(PowerConverter.VALID_UNITS), + vol.Optional("pressure"): vol.In(PressureConverter.VALID_UNITS), + vol.Optional("speed"): vol.In(SpeedConverter.VALID_UNITS), + vol.Optional("temperature"): vol.In(TemperatureConverter.VALID_UNITS), + vol.Optional("unitless"): vol.In(UnitlessRatioConverter.VALID_UNITS), + vol.Optional("volume"): vol.In(VolumeConverter.VALID_UNITS), + } +) + @callback def async_setup(hass: HomeAssistant) -> None: @@ -93,18 +116,7 @@ def _ws_get_statistic_during_period( vol.Optional("types"): vol.All( [vol.Any("max", "mean", "min", "change")], vol.Coerce(set) ), - vol.Optional("units"): vol.Schema( - { - vol.Optional("distance"): vol.In(DistanceConverter.VALID_UNITS), - vol.Optional("energy"): vol.In(EnergyConverter.VALID_UNITS), - vol.Optional("mass"): vol.In(MassConverter.VALID_UNITS), - vol.Optional("power"): vol.In(PowerConverter.VALID_UNITS), - vol.Optional("pressure"): vol.In(PressureConverter.VALID_UNITS), - vol.Optional("speed"): vol.In(SpeedConverter.VALID_UNITS), - vol.Optional("temperature"): vol.In(TemperatureConverter.VALID_UNITS), - vol.Optional("volume"): vol.In(VolumeConverter.VALID_UNITS), - } - ), + vol.Optional("units"): UNIT_SCHEMA, **PERIOD_SCHEMA.schema, } ) @@ -211,18 +223,7 @@ async def ws_handle_get_statistics_during_period( vol.Optional("end_time"): str, vol.Optional("statistic_ids"): [str], vol.Required("period"): vol.Any("5minute", "hour", "day", "week", "month"), - vol.Optional("units"): vol.Schema( - { - vol.Optional("distance"): vol.In(DistanceConverter.VALID_UNITS), - vol.Optional("energy"): vol.In(EnergyConverter.VALID_UNITS), - vol.Optional("mass"): vol.In(MassConverter.VALID_UNITS), - vol.Optional("power"): vol.In(PowerConverter.VALID_UNITS), - vol.Optional("pressure"): vol.In(PressureConverter.VALID_UNITS), - vol.Optional("speed"): vol.In(SpeedConverter.VALID_UNITS), - vol.Optional("temperature"): vol.In(TemperatureConverter.VALID_UNITS), - vol.Optional("volume"): vol.In(VolumeConverter.VALID_UNITS), - } - ), + vol.Optional("units"): UNIT_SCHEMA, vol.Optional("types"): vol.All( [vol.Any("last_reset", "max", "mean", "min", "state", "sum")], vol.Coerce(set), From bfcae4e07be9a3f9693f5104deea95d75bf90ef6 Mon Sep 17 00:00:00 2001 From: mkmer Date: Mon, 30 Jan 2023 07:57:14 -0500 Subject: [PATCH 09/14] Add Reauth config flow to honeywell (#86170) --- .../components/honeywell/__init__.py | 13 +- homeassistant/components/honeywell/climate.py | 1 - .../components/honeywell/config_flow.py | 58 +++++++ .../components/honeywell/test_config_flow.py | 145 +++++++++++++++++- 4 files changed, 204 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/honeywell/__init__.py b/homeassistant/components/honeywell/__init__.py index 3316d2852e7..c0ebcd9b1cf 100644 --- a/homeassistant/components/honeywell/__init__.py +++ b/homeassistant/components/honeywell/__init__.py @@ -7,7 +7,7 @@ import AIOSomecomfort from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.helpers.aiohttp_client import async_get_clientsession from .const import ( @@ -57,15 +57,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b await client.login() await client.discover() - except AIOSomecomfort.AuthError as ex: - raise ConfigEntryNotReady( - "Failed to initialize the Honeywell client: " - "Check your configuration (username, password), " - ) from ex + except AIOSomecomfort.device.AuthError as ex: + raise ConfigEntryAuthFailed("Incorrect Password") from ex except ( - AIOSomecomfort.ConnectionError, - AIOSomecomfort.ConnectionTimeout, + AIOSomecomfort.device.ConnectionError, + AIOSomecomfort.device.ConnectionTimeout, asyncio.TimeoutError, ) as ex: raise ConfigEntryNotReady( diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index 0267eb32e47..96fc30f57e0 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -345,7 +345,6 @@ class HoneywellUSThermostat(ClimateEntity): await self._device.set_setpoint_heat(self._heat_away_temp) except AIOSomecomfort.SomeComfortError: - _LOGGER.error( "Temperature out of range. Mode: %s, Heat Temperature: %.1f, Cool Temperature: %.1f", mode, diff --git a/homeassistant/components/honeywell/config_flow.py b/homeassistant/components/honeywell/config_flow.py index 9f630d90fbe..0f9d5663b3e 100644 --- a/homeassistant/components/honeywell/config_flow.py +++ b/homeassistant/components/honeywell/config_flow.py @@ -2,6 +2,8 @@ from __future__ import annotations import asyncio +from collections.abc import Mapping +from typing import Any import AIOSomecomfort import voluptuous as vol @@ -20,11 +22,67 @@ from .const import ( DOMAIN, ) +REAUTH_SCHEMA = vol.Schema({vol.Required(CONF_PASSWORD): str}) + class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Handle a honeywell config flow.""" VERSION = 1 + entry: config_entries.ConfigEntry | None + + async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult: + """Handle re-authentication with Honeywell.""" + + self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) + return await self.async_step_reauth_confirm() + + async def async_step_reauth_confirm( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Confirm re-authentication with Honeywell.""" + errors: dict[str, str] = {} + + if user_input: + assert self.entry is not None + password = user_input[CONF_PASSWORD] + data = { + CONF_USERNAME: self.entry.data[CONF_USERNAME], + CONF_PASSWORD: password, + } + + try: + await self.is_valid( + username=data[CONF_USERNAME], password=data[CONF_PASSWORD] + ) + + except AIOSomecomfort.AuthError: + errors["base"] = "invalid_auth" + + except ( + AIOSomecomfort.ConnectionError, + AIOSomecomfort.ConnectionTimeout, + asyncio.TimeoutError, + ): + errors["base"] = "cannot_connect" + + else: + + self.hass.config_entries.async_update_entry( + self.entry, + data={ + **self.entry.data, + CONF_PASSWORD: password, + }, + ) + await self.hass.config_entries.async_reload(self.entry.entry_id) + return self.async_abort(reason="reauth_successful") + + return self.async_show_form( + step_id="reauth_confirm", + data_schema=REAUTH_SCHEMA, + errors=errors, + ) async def async_step_user(self, user_input=None) -> FlowResult: """Create config entry. Show the setup form to the user.""" diff --git a/tests/components/honeywell/test_config_flow.py b/tests/components/honeywell/test_config_flow.py index 46ab48572f8..2d76962b028 100644 --- a/tests/components/honeywell/test_config_flow.py +++ b/tests/components/honeywell/test_config_flow.py @@ -1,7 +1,9 @@ """Tests for honeywell config flow.""" +import asyncio from unittest.mock import MagicMock, patch import AIOSomecomfort +import pytest from homeassistant import data_entry_flow from homeassistant.components.honeywell.const import ( @@ -9,8 +11,10 @@ from homeassistant.components.honeywell.const import ( CONF_HEAT_AWAY_TEMPERATURE, DOMAIN, ) -from homeassistant.config_entries import SOURCE_USER, ConfigEntryState +from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER, ConfigEntryState +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType from tests.common import MockConfigEntry @@ -35,8 +39,7 @@ async def test_show_authenticate_form(hass: HomeAssistant) -> None: async def test_connection_error(hass: HomeAssistant, client: MagicMock) -> None: """Test that an error message is shown on connection fail.""" - client.login.side_effect = AIOSomecomfort.ConnectionError - + client.login.side_effect = AIOSomecomfort.device.ConnectionError result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=FAKE_CONFIG ) @@ -45,7 +48,7 @@ async def test_connection_error(hass: HomeAssistant, client: MagicMock) -> None: async def test_auth_error(hass: HomeAssistant, client: MagicMock) -> None: """Test that an error message is shown on login fail.""" - client.login.side_effect = AIOSomecomfort.AuthError + client.login.side_effect = AIOSomecomfort.device.AuthError result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=FAKE_CONFIG @@ -116,3 +119,137 @@ async def test_create_option_entry( CONF_COOL_AWAY_TEMPERATURE: 1, CONF_HEAT_AWAY_TEMPERATURE: 2, } + + +async def test_reauth_flow(hass: HomeAssistant) -> None: + """Test a successful reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + with patch( + "homeassistant.components.honeywell.async_setup_entry", + return_value=True, + ): + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "new-password"}, + ) + + await hass.async_block_till_done() + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {} + + with patch( + "homeassistant.components.honeywell.async_setup_entry", + return_value=True, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.ABORT + assert result2["reason"] == "reauth_successful" + assert mock_entry.data == { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "new-password", + } + + +async def test_reauth_flow_auth_error(hass: HomeAssistant, client: MagicMock) -> None: + """Test an authorization error reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {} + + client.login.side_effect = AIOSomecomfort.device.AuthError + with patch( + "homeassistant.components.honeywell.async_setup_entry", + return_value=True, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "invalid_auth"} + + +@pytest.mark.parametrize( + "error", + [ + AIOSomecomfort.device.ConnectionError, + AIOSomecomfort.device.ConnectionTimeout, + asyncio.TimeoutError, + ], +) +async def test_reauth_flow_connnection_error( + hass: HomeAssistant, client: MagicMock, error +) -> None: + """Test a connection error reauth flow.""" + + mock_entry = MockConfigEntry( + domain=DOMAIN, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + unique_id="test-username", + ) + mock_entry.add_to_hass(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={ + "source": SOURCE_REAUTH, + "unique_id": mock_entry.unique_id, + "entry_id": mock_entry.entry_id, + }, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result["step_id"] == "reauth_confirm" + assert result["type"] == FlowResultType.FORM + assert result["errors"] == {} + + client.login.side_effect = error + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_PASSWORD: "new-password"}, + ) + await hass.async_block_till_done() + + assert result2["type"] == FlowResultType.FORM + assert result2["errors"] == {"base": "cannot_connect"} From 063bbe91d152c2dc901f96e4aa616a15c785fd8a Mon Sep 17 00:00:00 2001 From: mkmer Date: Thu, 2 Feb 2023 16:45:49 -0500 Subject: [PATCH 10/14] Bump AIOSomecomfort to 0.0.6 (#87203) * Bump to 0.0.5 * Bump aiosomecomfort to 0.0.6 * lower case aiosomecomfort * Fix other bad imports.... --------- Co-authored-by: Paulus Schoutsen --- .../components/honeywell/__init__.py | 14 ++++++------ homeassistant/components/honeywell/climate.py | 22 +++++++++---------- .../components/honeywell/config_flow.py | 16 +++++++------- .../components/honeywell/manifest.json | 2 +- homeassistant/components/honeywell/sensor.py | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/honeywell/conftest.py | 14 ++++++------ .../components/honeywell/test_config_flow.py | 12 +++++----- tests/components/honeywell/test_init.py | 4 ++-- tests/components/honeywell/test_sensor.py | 4 ++-- 11 files changed, 47 insertions(+), 47 deletions(-) diff --git a/homeassistant/components/honeywell/__init__.py b/homeassistant/components/honeywell/__init__.py index c0ebcd9b1cf..93c29446a53 100644 --- a/homeassistant/components/honeywell/__init__.py +++ b/homeassistant/components/honeywell/__init__.py @@ -2,7 +2,7 @@ import asyncio from dataclasses import dataclass -import AIOSomecomfort +import aiosomecomfort from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform @@ -50,19 +50,19 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b username = config_entry.data[CONF_USERNAME] password = config_entry.data[CONF_PASSWORD] - client = AIOSomecomfort.AIOSomeComfort( + client = aiosomecomfort.AIOSomeComfort( username, password, session=async_get_clientsession(hass) ) try: await client.login() await client.discover() - except AIOSomecomfort.device.AuthError as ex: + except aiosomecomfort.device.AuthError as ex: raise ConfigEntryAuthFailed("Incorrect Password") from ex except ( - AIOSomecomfort.device.ConnectionError, - AIOSomecomfort.device.ConnectionTimeout, + aiosomecomfort.device.ConnectionError, + aiosomecomfort.device.ConnectionTimeout, asyncio.TimeoutError, ) as ex: raise ConfigEntryNotReady( @@ -114,5 +114,5 @@ class HoneywellData: """Shared data for Honeywell.""" entry_id: str - client: AIOSomecomfort.AIOSomeComfort - devices: dict[str, AIOSomecomfort.device.Device] + client: aiosomecomfort.AIOSomeComfort + devices: dict[str, aiosomecomfort.device.Device] diff --git a/homeassistant/components/honeywell/climate.py b/homeassistant/components/honeywell/climate.py index 96fc30f57e0..1296dfbfdb3 100644 --- a/homeassistant/components/honeywell/climate.py +++ b/homeassistant/components/honeywell/climate.py @@ -4,7 +4,7 @@ from __future__ import annotations import datetime from typing import Any -import AIOSomecomfort +import aiosomecomfort from homeassistant.components.climate import ( ATTR_TARGET_TEMP_HIGH, @@ -100,7 +100,7 @@ class HoneywellUSThermostat(ClimateEntity): def __init__( self, data: HoneywellData, - device: AIOSomecomfort.device.Device, + device: aiosomecomfort.device.Device, cool_away_temp: int | None, heat_away_temp: int | None, ) -> None: @@ -295,7 +295,7 @@ class HoneywellUSThermostat(ClimateEntity): if mode == "heat": await self._device.set_setpoint_heat(temperature) - except AIOSomecomfort.SomeComfortError as err: + except aiosomecomfort.SomeComfortError as err: _LOGGER.error("Invalid temperature %.1f: %s", temperature, err) async def async_set_temperature(self, **kwargs: Any) -> None: @@ -308,7 +308,7 @@ class HoneywellUSThermostat(ClimateEntity): if temperature := kwargs.get(ATTR_TARGET_TEMP_LOW): await self._device.set_setpoint_heat(temperature) - except AIOSomecomfort.SomeComfortError as err: + except aiosomecomfort.SomeComfortError as err: _LOGGER.error("Invalid temperature %.1f: %s", temperature, err) async def async_set_fan_mode(self, fan_mode: str) -> None: @@ -330,7 +330,7 @@ class HoneywellUSThermostat(ClimateEntity): try: # Get current mode mode = self._device.system_mode - except AIOSomecomfort.SomeComfortError: + except aiosomecomfort.SomeComfortError: _LOGGER.error("Can not get system mode") return try: @@ -344,7 +344,7 @@ class HoneywellUSThermostat(ClimateEntity): await self._device.set_hold_heat(True) await self._device.set_setpoint_heat(self._heat_away_temp) - except AIOSomecomfort.SomeComfortError: + except aiosomecomfort.SomeComfortError: _LOGGER.error( "Temperature out of range. Mode: %s, Heat Temperature: %.1f, Cool Temperature: %.1f", mode, @@ -357,7 +357,7 @@ class HoneywellUSThermostat(ClimateEntity): try: # Get current mode mode = self._device.system_mode - except AIOSomecomfort.SomeComfortError: + except aiosomecomfort.SomeComfortError: _LOGGER.error("Can not get system mode") return # Check that we got a valid mode back @@ -369,7 +369,7 @@ class HoneywellUSThermostat(ClimateEntity): if mode in HEATING_MODES: await self._device.set_hold_heat(True) - except AIOSomecomfort.SomeComfortError: + except aiosomecomfort.SomeComfortError: _LOGGER.error("Couldn't set permanent hold") else: _LOGGER.error("Invalid system mode returned: %s", mode) @@ -381,7 +381,7 @@ class HoneywellUSThermostat(ClimateEntity): # Disabling all hold modes await self._device.set_hold_cool(False) await self._device.set_hold_heat(False) - except AIOSomecomfort.SomeComfortError: + except aiosomecomfort.SomeComfortError: _LOGGER.error("Can not stop hold mode") async def async_set_preset_mode(self, preset_mode: str) -> None: @@ -410,13 +410,13 @@ class HoneywellUSThermostat(ClimateEntity): try: await self._device.refresh() except ( - AIOSomecomfort.SomeComfortError, + aiosomecomfort.SomeComfortError, OSError, ): try: await self._data.client.login() - except AIOSomecomfort.SomeComfortError: + except aiosomecomfort.SomeComfortError: self._attr_available = False await self.hass.async_create_task( self.hass.config_entries.async_reload(self._data.entry_id) diff --git a/homeassistant/components/honeywell/config_flow.py b/homeassistant/components/honeywell/config_flow.py index 0f9d5663b3e..76d208f72f3 100644 --- a/homeassistant/components/honeywell/config_flow.py +++ b/homeassistant/components/honeywell/config_flow.py @@ -5,7 +5,7 @@ import asyncio from collections.abc import Mapping from typing import Any -import AIOSomecomfort +import aiosomecomfort import voluptuous as vol from homeassistant import config_entries @@ -56,12 +56,12 @@ class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): username=data[CONF_USERNAME], password=data[CONF_PASSWORD] ) - except AIOSomecomfort.AuthError: + except aiosomecomfort.AuthError: errors["base"] = "invalid_auth" except ( - AIOSomecomfort.ConnectionError, - AIOSomecomfort.ConnectionTimeout, + aiosomecomfort.ConnectionError, + aiosomecomfort.ConnectionTimeout, asyncio.TimeoutError, ): errors["base"] = "cannot_connect" @@ -90,11 +90,11 @@ class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if user_input is not None: try: await self.is_valid(**user_input) - except AIOSomecomfort.AuthError: + except aiosomecomfort.AuthError: errors["base"] = "invalid_auth" except ( - AIOSomecomfort.ConnectionError, - AIOSomecomfort.ConnectionTimeout, + aiosomecomfort.ConnectionError, + aiosomecomfort.ConnectionTimeout, asyncio.TimeoutError, ): errors["base"] = "cannot_connect" @@ -115,7 +115,7 @@ class HoneywellConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): async def is_valid(self, **kwargs) -> bool: """Check if login credentials are valid.""" - client = AIOSomecomfort.AIOSomeComfort( + client = aiosomecomfort.AIOSomeComfort( kwargs[CONF_USERNAME], kwargs[CONF_PASSWORD], session=async_get_clientsession(self.hass), diff --git a/homeassistant/components/honeywell/manifest.json b/homeassistant/components/honeywell/manifest.json index 7eb07711b09..974123825fa 100644 --- a/homeassistant/components/honeywell/manifest.json +++ b/homeassistant/components/honeywell/manifest.json @@ -3,7 +3,7 @@ "name": "Honeywell Total Connect Comfort (US)", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/honeywell", - "requirements": ["aiosomecomfort==0.0.3"], + "requirements": ["aiosomecomfort==0.0.6"], "codeowners": ["@rdfurman", "@mkmer"], "iot_class": "cloud_polling", "loggers": ["somecomfort"] diff --git a/homeassistant/components/honeywell/sensor.py b/homeassistant/components/honeywell/sensor.py index 59f00472700..9e85ba7727a 100644 --- a/homeassistant/components/honeywell/sensor.py +++ b/homeassistant/components/honeywell/sensor.py @@ -5,7 +5,7 @@ from collections.abc import Callable from dataclasses import dataclass from typing import Any -from AIOSomecomfort.device import Device +from aiosomecomfort.device import Device from homeassistant.components.sensor import ( SensorDeviceClass, diff --git a/requirements_all.txt b/requirements_all.txt index f8abf9d975b..7f51ac35817 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -276,7 +276,7 @@ aioskybell==22.7.0 aioslimproto==2.1.1 # homeassistant.components.honeywell -aiosomecomfort==0.0.3 +aiosomecomfort==0.0.6 # homeassistant.components.steamist aiosteamist==0.3.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 18071e51a33..341b688b098 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -254,7 +254,7 @@ aioskybell==22.7.0 aioslimproto==2.1.1 # homeassistant.components.honeywell -aiosomecomfort==0.0.3 +aiosomecomfort==0.0.6 # homeassistant.components.steamist aiosteamist==0.3.2 diff --git a/tests/components/honeywell/conftest.py b/tests/components/honeywell/conftest.py index bead64c71d1..95e1758ec22 100644 --- a/tests/components/honeywell/conftest.py +++ b/tests/components/honeywell/conftest.py @@ -2,7 +2,7 @@ from unittest.mock import AsyncMock, create_autospec, patch -import AIOSomecomfort +import aiosomecomfort import pytest from homeassistant.components.honeywell.const import DOMAIN @@ -30,7 +30,7 @@ def config_entry(config_data): @pytest.fixture def device(): """Mock a somecomfort.Device.""" - mock_device = create_autospec(AIOSomecomfort.device.Device, instance=True) + mock_device = create_autospec(aiosomecomfort.device.Device, instance=True) mock_device.deviceid = 1234567 mock_device._data = { "canControlHumidification": False, @@ -48,7 +48,7 @@ def device(): @pytest.fixture def device_with_outdoor_sensor(): """Mock a somecomfort.Device.""" - mock_device = create_autospec(AIOSomecomfort.device.Device, instance=True) + mock_device = create_autospec(aiosomecomfort.device.Device, instance=True) mock_device.deviceid = 1234567 mock_device._data = { "canControlHumidification": False, @@ -67,7 +67,7 @@ def device_with_outdoor_sensor(): @pytest.fixture def another_device(): """Mock a somecomfort.Device.""" - mock_device = create_autospec(AIOSomecomfort.device.Device, instance=True) + mock_device = create_autospec(aiosomecomfort.device.Device, instance=True) mock_device.deviceid = 7654321 mock_device._data = { "canControlHumidification": False, @@ -85,7 +85,7 @@ def another_device(): @pytest.fixture def location(device): """Mock a somecomfort.Location.""" - mock_location = create_autospec(AIOSomecomfort.location.Location, instance=True) + mock_location = create_autospec(aiosomecomfort.location.Location, instance=True) mock_location.locationid.return_value = "location1" mock_location.devices_by_id = {device.deviceid: device} return mock_location @@ -94,13 +94,13 @@ def location(device): @pytest.fixture(autouse=True) def client(location): """Mock a somecomfort.SomeComfort client.""" - client_mock = create_autospec(AIOSomecomfort.AIOSomeComfort, instance=True) + client_mock = create_autospec(aiosomecomfort.AIOSomeComfort, instance=True) client_mock.locations_by_id = {location.locationid: location} client_mock.login = AsyncMock(return_value=True) client_mock.discover = AsyncMock() with patch( - "homeassistant.components.honeywell.AIOSomecomfort.AIOSomeComfort" + "homeassistant.components.honeywell.aiosomecomfort.AIOSomeComfort" ) as sc_class_mock: sc_class_mock.return_value = client_mock yield client_mock diff --git a/tests/components/honeywell/test_config_flow.py b/tests/components/honeywell/test_config_flow.py index 2d76962b028..ff970a0e8c5 100644 --- a/tests/components/honeywell/test_config_flow.py +++ b/tests/components/honeywell/test_config_flow.py @@ -2,7 +2,7 @@ import asyncio from unittest.mock import MagicMock, patch -import AIOSomecomfort +import aiosomecomfort import pytest from homeassistant import data_entry_flow @@ -39,7 +39,7 @@ async def test_show_authenticate_form(hass: HomeAssistant) -> None: async def test_connection_error(hass: HomeAssistant, client: MagicMock) -> None: """Test that an error message is shown on connection fail.""" - client.login.side_effect = AIOSomecomfort.device.ConnectionError + client.login.side_effect = aiosomecomfort.device.ConnectionError result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=FAKE_CONFIG ) @@ -48,7 +48,7 @@ async def test_connection_error(hass: HomeAssistant, client: MagicMock) -> None: async def test_auth_error(hass: HomeAssistant, client: MagicMock) -> None: """Test that an error message is shown on login fail.""" - client.login.side_effect = AIOSomecomfort.device.AuthError + client.login.side_effect = aiosomecomfort.device.AuthError result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=FAKE_CONFIG @@ -194,7 +194,7 @@ async def test_reauth_flow_auth_error(hass: HomeAssistant, client: MagicMock) -> assert result["type"] == FlowResultType.FORM assert result["errors"] == {} - client.login.side_effect = AIOSomecomfort.device.AuthError + client.login.side_effect = aiosomecomfort.device.AuthError with patch( "homeassistant.components.honeywell.async_setup_entry", return_value=True, @@ -212,8 +212,8 @@ async def test_reauth_flow_auth_error(hass: HomeAssistant, client: MagicMock) -> @pytest.mark.parametrize( "error", [ - AIOSomecomfort.device.ConnectionError, - AIOSomecomfort.device.ConnectionTimeout, + aiosomecomfort.device.ConnectionError, + aiosomecomfort.device.ConnectionTimeout, asyncio.TimeoutError, ], ) diff --git a/tests/components/honeywell/test_init.py b/tests/components/honeywell/test_init.py index 4ecd2a3172d..855d503401e 100644 --- a/tests/components/honeywell/test_init.py +++ b/tests/components/honeywell/test_init.py @@ -2,7 +2,7 @@ from unittest.mock import create_autospec, patch -import AIOSomecomfort +import aiosomecomfort from homeassistant.components.honeywell.const import ( CONF_COOL_AWAY_TEMPERATURE, @@ -46,7 +46,7 @@ async def test_setup_multiple_thermostats_with_same_deviceid( hass: HomeAssistant, caplog, config_entry: MockConfigEntry, device, client ) -> None: """Test Honeywell TCC API returning duplicate device IDs.""" - mock_location2 = create_autospec(AIOSomecomfort.Location, instance=True) + mock_location2 = create_autospec(aiosomecomfort.Location, instance=True) mock_location2.locationid.return_value = "location2" mock_location2.devices_by_id = {device.deviceid: device} client.locations_by_id["location2"] = mock_location2 diff --git a/tests/components/honeywell/test_sensor.py b/tests/components/honeywell/test_sensor.py index 7ed047262bf..c5367764d3d 100644 --- a/tests/components/honeywell/test_sensor.py +++ b/tests/components/honeywell/test_sensor.py @@ -1,6 +1,6 @@ """Test honeywell sensor.""" -from AIOSomecomfort.device import Device -from AIOSomecomfort.location import Location +from aiosomecomfort.device import Device +from aiosomecomfort.location import Location import pytest from homeassistant.core import HomeAssistant From 75796e1f0f3ea6d65da141779121dcc1deaea08a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Thu, 2 Feb 2023 22:11:01 +0100 Subject: [PATCH 11/14] Update frontend to 20230202.0 (#87208) --- 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 b33668cde4c..ba4185fa838 100644 --- a/homeassistant/components/frontend/manifest.json +++ b/homeassistant/components/frontend/manifest.json @@ -2,7 +2,7 @@ "domain": "frontend", "name": "Home Assistant Frontend", "documentation": "https://www.home-assistant.io/integrations/frontend", - "requirements": ["home-assistant-frontend==20230201.0"], + "requirements": ["home-assistant-frontend==20230202.0"], "dependencies": [ "api", "auth", diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 9101375adb3..8dc60525be1 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -23,7 +23,7 @@ fnvhash==0.1.0 hass-nabucasa==0.61.0 hassil==0.2.6 home-assistant-bluetooth==1.9.2 -home-assistant-frontend==20230201.0 +home-assistant-frontend==20230202.0 home-assistant-intents==2023.1.31 httpx==0.23.3 ifaddr==0.1.7 diff --git a/requirements_all.txt b/requirements_all.txt index 7f51ac35817..c4c62b9c8f8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -907,7 +907,7 @@ hole==0.8.0 holidays==0.18.0 # homeassistant.components.frontend -home-assistant-frontend==20230201.0 +home-assistant-frontend==20230202.0 # homeassistant.components.conversation home-assistant-intents==2023.1.31 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 341b688b098..4db8dac349b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -690,7 +690,7 @@ hole==0.8.0 holidays==0.18.0 # homeassistant.components.frontend -home-assistant-frontend==20230201.0 +home-assistant-frontend==20230202.0 # homeassistant.components.conversation home-assistant-intents==2023.1.31 From 1d8f5b2e163ef4ec0935dc35bd86a77bcc90c352 Mon Sep 17 00:00:00 2001 From: Michael <35783820+mib1185@users.noreply.github.com> Date: Thu, 2 Feb 2023 22:10:51 +0100 Subject: [PATCH 12/14] Bump py-synologydsm-api to 2.1.1 (#87211) bump py-synologydsm-api to 2.1.1 --- homeassistant/components/synology_dsm/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/synology_dsm/manifest.json b/homeassistant/components/synology_dsm/manifest.json index 6d0012156db..14f45fbf637 100644 --- a/homeassistant/components/synology_dsm/manifest.json +++ b/homeassistant/components/synology_dsm/manifest.json @@ -2,7 +2,7 @@ "domain": "synology_dsm", "name": "Synology DSM", "documentation": "https://www.home-assistant.io/integrations/synology_dsm", - "requirements": ["py-synologydsm-api==2.0.2"], + "requirements": ["py-synologydsm-api==2.1.1"], "codeowners": ["@hacf-fr", "@Quentame", "@mib1185"], "config_flow": true, "ssdp": [ diff --git a/requirements_all.txt b/requirements_all.txt index c4c62b9c8f8..ac62e334ec8 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1442,7 +1442,7 @@ py-schluter==0.1.7 py-sucks==0.9.8 # homeassistant.components.synology_dsm -py-synologydsm-api==2.0.2 +py-synologydsm-api==2.1.1 # homeassistant.components.zabbix py-zabbix==1.1.7 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 4db8dac349b..52727f66ee4 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1051,7 +1051,7 @@ py-melissa-climate==2.1.4 py-nightscout==1.2.2 # homeassistant.components.synology_dsm -py-synologydsm-api==2.0.2 +py-synologydsm-api==2.1.1 # homeassistant.components.seventeentrack py17track==2021.12.2 From ed8a0ef0eae239dfe465305f5964bf8e94c7b600 Mon Sep 17 00:00:00 2001 From: Karlie Meads <68717336+karliemeads@users.noreply.github.com> Date: Thu, 2 Feb 2023 16:35:02 -0500 Subject: [PATCH 13/14] Fix disabled condition within an automation action (#87213) fixes undefined --- homeassistant/helpers/script.py | 2 +- tests/helpers/test_script.py | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/homeassistant/helpers/script.py b/homeassistant/helpers/script.py index 34a0d4de2d4..02fa9dc7806 100644 --- a/homeassistant/helpers/script.py +++ b/homeassistant/helpers/script.py @@ -757,7 +757,7 @@ class _ScriptRun: with trace_path(condition_path): for idx, cond in enumerate(conditions): with trace_path(str(idx)): - if not cond(hass, variables): + if cond(hass, variables) is False: return False except exceptions.ConditionError as ex: _LOGGER.warning("Error in '%s[%s]' evaluation: %s", name, idx, ex) diff --git a/tests/helpers/test_script.py b/tests/helpers/test_script.py index b44e7b7c458..86172c6c7fd 100644 --- a/tests/helpers/test_script.py +++ b/tests/helpers/test_script.py @@ -2915,6 +2915,45 @@ async def test_if( assert_action_trace(expected_trace) +async def test_if_disabled( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test if action with a disabled condition.""" + sequence = cv.SCRIPT_SCHEMA( + { + "if": { + "alias": "if condition", + "condition": "template", + "value_template": "{{ var == 1 }}", + "enabled": "false", + }, + "then": { + "alias": "if then", + "event": "test_event", + "event_data": {"if": "then"}, + }, + "else": { + "alias": "if else", + "event": "test_event", + "event_data": {"if": "else"}, + }, + } + ) + + script_obj = script.Script(hass, sequence, "Test Name", "test_domain") + + await script_obj.async_run(context=Context()) + await hass.async_block_till_done() + + expected_trace = { + "0": [{"result": {"choice": "then"}}], + "0/if": [{"result": {"result": True}}], + "0/if/condition/0": [{"result": {"result": None}}], + "0/then/0": [{"result": {"event": "test_event", "event_data": {"if": "then"}}}], + } + assert_action_trace(expected_trace) + + async def test_if_condition_validation( hass: HomeAssistant, caplog: pytest.LogCaptureFixture ) -> None: From 372afc5c28483b6336f300728149d8a046e09316 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 2 Feb 2023 16:48:09 -0500 Subject: [PATCH 14/14] Bumped version to 2023.2.1 --- homeassistant/const.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/const.py b/homeassistant/const.py index ee5d60a32be..070f9a37862 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -8,7 +8,7 @@ from .backports.enum import StrEnum APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2023 MINOR_VERSION: Final = 2 -PATCH_VERSION: Final = "0" +PATCH_VERSION: Final = "1" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 10, 0) diff --git a/pyproject.toml b/pyproject.toml index 5c014aabd4b..affc4b6446c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2023.2.0" +version = "2023.2.1" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst"