mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 19:27:45 +00:00
2023.2.1 (#87221)
This commit is contained in:
commit
dbd8ffc282
@ -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",
|
||||
|
@ -2,12 +2,12 @@
|
||||
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
|
||||
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 (
|
||||
@ -50,22 +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.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(
|
||||
@ -117,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]
|
||||
|
@ -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,8 +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,
|
||||
@ -358,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
|
||||
@ -370,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)
|
||||
@ -382,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:
|
||||
@ -411,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)
|
||||
|
@ -2,8 +2,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
import AIOSomecomfort
|
||||
import aiosomecomfort
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
@ -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."""
|
||||
@ -32,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"
|
||||
@ -57,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),
|
||||
|
@ -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"]
|
||||
|
@ -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,
|
||||
|
@ -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"],
|
||||
|
@ -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},
|
||||
|
@ -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),
|
||||
|
@ -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",
|
||||
|
@ -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(
|
||||
@ -90,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,
|
||||
|
@ -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,
|
||||
@ -246,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(
|
||||
|
@ -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.2"],
|
||||
"dependencies": ["webhook"],
|
||||
"codeowners": ["@starkillerOG"],
|
||||
"iot_class": "local_polling",
|
||||
|
@ -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": [
|
||||
|
@ -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."""
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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.2
|
||||
|
||||
# homeassistant.components.python_script
|
||||
restrictedpython==6.0
|
||||
|
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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.2
|
||||
|
||||
# homeassistant.components.python_script
|
||||
restrictedpython==6.0
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,9 @@
|
||||
"""Tests for honeywell config flow."""
|
||||
import asyncio
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import AIOSomecomfort
|
||||
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"}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user