Merge pull request #71535 from home-assistant/rc

pull/71566/head 2022.5.3
Paulus Schoutsen 2022-05-08 11:13:45 -07:00 committed by GitHub
commit b5bd154e87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 978 additions and 48 deletions

View File

@ -1320,6 +1320,9 @@ omit =
homeassistant/components/twitter/notify.py homeassistant/components/twitter/notify.py
homeassistant/components/ubus/device_tracker.py homeassistant/components/ubus/device_tracker.py
homeassistant/components/ue_smart_radio/media_player.py homeassistant/components/ue_smart_radio/media_player.py
homeassistant/components/ukraine_alarm/__init__.py
homeassistant/components/ukraine_alarm/const.py
homeassistant/components/ukraine_alarm/binary_sensor.py
homeassistant/components/unifiled/* homeassistant/components/unifiled/*
homeassistant/components/upb/__init__.py homeassistant/components/upb/__init__.py
homeassistant/components/upb/const.py homeassistant/components/upb/const.py

View File

@ -1070,6 +1070,8 @@ build.json @home-assistant/supervisor
/tests/components/twentemilieu/ @frenck /tests/components/twentemilieu/ @frenck
/homeassistant/components/twinkly/ @dr1rrb @Robbie1221 /homeassistant/components/twinkly/ @dr1rrb @Robbie1221
/tests/components/twinkly/ @dr1rrb @Robbie1221 /tests/components/twinkly/ @dr1rrb @Robbie1221
/homeassistant/components/ukraine_alarm/ @PaulAnnekov
/tests/components/ukraine_alarm/ @PaulAnnekov
/homeassistant/components/unifi/ @Kane610 /homeassistant/components/unifi/ @Kane610
/tests/components/unifi/ @Kane610 /tests/components/unifi/ @Kane610
/homeassistant/components/unifiled/ @florisvdk /homeassistant/components/unifiled/ @florisvdk

View File

@ -4,6 +4,7 @@ from __future__ import annotations
from datetime import timedelta from datetime import timedelta
import logging import logging
import async_timeout
from brother import Brother, DictToObj, SnmpError, UnsupportedModel from brother import Brother, DictToObj, SnmpError, UnsupportedModel
import pysnmp.hlapi.asyncio as SnmpEngine import pysnmp.hlapi.asyncio as SnmpEngine
@ -76,6 +77,7 @@ class BrotherDataUpdateCoordinator(DataUpdateCoordinator):
async def _async_update_data(self) -> DictToObj: async def _async_update_data(self) -> DictToObj:
"""Update data via library.""" """Update data via library."""
try: try:
async with async_timeout.timeout(20):
data = await self.brother.async_update() data = await self.brother.async_update()
except (ConnectionError, SnmpError, UnsupportedModel) as error: except (ConnectionError, SnmpError, UnsupportedModel) as error:
raise UpdateFailed(error) from error raise UpdateFailed(error) from error

View File

@ -2,7 +2,7 @@
"domain": "canary", "domain": "canary",
"name": "Canary", "name": "Canary",
"documentation": "https://www.home-assistant.io/integrations/canary", "documentation": "https://www.home-assistant.io/integrations/canary",
"requirements": ["py-canary==0.5.1"], "requirements": ["py-canary==0.5.2"],
"dependencies": ["ffmpeg"], "dependencies": ["ffmpeg"],
"codeowners": [], "codeowners": [],
"config_flow": true, "config_flow": true,

View File

@ -10,10 +10,7 @@ from fiblary3.client.v4.client import (
Client as FibaroClientV4, Client as FibaroClientV4,
StateHandler as StateHandlerV4, StateHandler as StateHandlerV4,
) )
from fiblary3.client.v5.client import ( from fiblary3.client.v5.client import StateHandler as StateHandlerV5
Client as FibaroClientV5,
StateHandler as StateHandlerV5,
)
from fiblary3.common.exceptions import HTTPException from fiblary3.common.exceptions import HTTPException
import voluptuous as vol import voluptuous as vol
@ -141,18 +138,12 @@ class FibaroController:
should do that only when you use the FibaroController for login test as only should do that only when you use the FibaroController for login test as only
the login and info API's are equal throughout the different versions. the login and info API's are equal throughout the different versions.
""" """
if (
serial_number is None # Only use V4 API as it works better even for HC3, after the library is fixed, we should
or serial_number.upper().startswith("HC2") # add here support for the newer library version V5 again.
or serial_number.upper().startswith("HCL")
):
self._client = FibaroClientV4( self._client = FibaroClientV4(
config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD] config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD]
) )
else:
self._client = FibaroClientV5(
config[CONF_URL], config[CONF_USERNAME], config[CONF_PASSWORD]
)
self._scene_map = None self._scene_map = None
# Whether to import devices from plugins # Whether to import devices from plugins

View File

@ -185,6 +185,6 @@ class FibaroLight(FibaroDevice, LightEntity):
rgbw_list = [int(i) for i in rgbw_s.split(",")][:4] rgbw_list = [int(i) for i in rgbw_s.split(",")][:4]
if self._attr_color_mode == ColorMode.RGB: if self._attr_color_mode == ColorMode.RGB:
self._attr_rgb_color = tuple(*rgbw_list[:3]) self._attr_rgb_color = tuple(rgbw_list[:3])
else: else:
self._attr_rgbw_color = tuple(rgbw_list) self._attr_rgbw_color = tuple(rgbw_list)

View File

@ -6,7 +6,11 @@ import logging
import voluptuous as vol import voluptuous as vol
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity
from homeassistant.components.climate.const import ClimateEntityFeature, HVACMode from homeassistant.components.climate.const import (
ClimateEntityFeature,
HVACAction,
HVACMode,
)
from homeassistant.components.modbus import get_hub from homeassistant.components.modbus import get_hub
from homeassistant.components.modbus.const import ( from homeassistant.components.modbus.const import (
CALL_TYPE_REGISTER_HOLDING, CALL_TYPE_REGISTER_HOLDING,
@ -69,9 +73,7 @@ class Flexit(ClimateEntity):
self._target_temperature = None self._target_temperature = None
self._current_temperature = None self._current_temperature = None
self._current_fan_mode = None self._current_fan_mode = None
self._current_operation = None
self._fan_modes = ["Off", "Low", "Medium", "High"] self._fan_modes = ["Off", "Low", "Medium", "High"]
self._current_operation = None
self._filter_hours = None self._filter_hours = None
self._filter_alarm = None self._filter_alarm = None
self._heat_recovery = None self._heat_recovery = None
@ -124,15 +126,15 @@ class Flexit(ClimateEntity):
) )
if self._heating: if self._heating:
self._current_operation = "Heating" self._attr_hvac_action = HVACAction.HEATING
elif self._cooling: elif self._cooling:
self._current_operation = "Cooling" self._attr_hvac_action = HVACAction.COOLING
elif self._heat_recovery: elif self._heat_recovery:
self._current_operation = "Recovering" self._attr_hvac_action = HVACAction.IDLE
elif actual_air_speed: elif actual_air_speed:
self._current_operation = "Fan Only" self._attr_hvac_action = HVACAction.FAN
else: else:
self._current_operation = "Off" self._attr_hvac_action = HVACAction.OFF
@property @property
def extra_state_attributes(self): def extra_state_attributes(self):
@ -175,7 +177,7 @@ class Flexit(ClimateEntity):
@property @property
def hvac_mode(self): def hvac_mode(self):
"""Return current operation ie. heat, cool, idle.""" """Return current operation ie. heat, cool, idle."""
return self._current_operation return HVACMode.COOL
@property @property
def hvac_modes(self) -> list[str]: def hvac_modes(self) -> list[str]:

View File

@ -2,7 +2,7 @@
"domain": "frontend", "domain": "frontend",
"name": "Home Assistant Frontend", "name": "Home Assistant Frontend",
"documentation": "https://www.home-assistant.io/integrations/frontend", "documentation": "https://www.home-assistant.io/integrations/frontend",
"requirements": ["home-assistant-frontend==20220504.0"], "requirements": ["home-assistant-frontend==20220504.1"],
"dependencies": [ "dependencies": [
"api", "api",
"auth", "auth",

View File

@ -32,13 +32,15 @@ class SabnzbdSensorEntityDescription(SensorEntityDescription, SabnzbdRequiredKey
"""Describes Sabnzbd sensor entity.""" """Describes Sabnzbd sensor entity."""
SPEED_KEY = "kbpersec"
SENSOR_TYPES: tuple[SabnzbdSensorEntityDescription, ...] = ( SENSOR_TYPES: tuple[SabnzbdSensorEntityDescription, ...] = (
SabnzbdSensorEntityDescription( SabnzbdSensorEntityDescription(
key="status", key="status",
name="Status", name="Status",
), ),
SabnzbdSensorEntityDescription( SabnzbdSensorEntityDescription(
key="kbpersec", key=SPEED_KEY,
name="Speed", name="Speed",
native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND, native_unit_of_measurement=DATA_RATE_MEGABYTES_PER_SECOND,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
@ -154,7 +156,7 @@ class SabnzbdSensor(SensorEntity):
self.entity_description.key self.entity_description.key
) )
if self.entity_description.key == "speed": if self.entity_description.key == SPEED_KEY:
self._attr_native_value = round(float(self._attr_native_value) / 1024, 1) self._attr_native_value = round(float(self._attr_native_value) / 1024, 1)
elif "size" in self.entity_description.key: elif "size" in self.entity_description.key:
self._attr_native_value = round(float(self._attr_native_value), 2) self._attr_native_value = round(float(self._attr_native_value), 2)

View File

@ -7,8 +7,14 @@ from homeassistant.core import HomeAssistant
from .const import PLATFORMS from .const import PLATFORMS
async def async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Update listener for options."""
await hass.config_entries.async_reload(entry.entry_id)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up SQL from a config entry.""" """Set up SQL from a config entry."""
entry.async_on_unload(entry.add_update_listener(async_update_listener))
hass.config_entries.async_setup_platforms(entry, PLATFORMS) hass.config_entries.async_setup_platforms(entry, PLATFORMS)

View File

@ -26,7 +26,9 @@ DATA_SCHEMA = vol.Schema(
vol.Required(CONF_NAME, default="Select SQL Query"): selector.TextSelector(), vol.Required(CONF_NAME, default="Select SQL Query"): selector.TextSelector(),
vol.Optional(CONF_DB_URL): selector.TextSelector(), vol.Optional(CONF_DB_URL): selector.TextSelector(),
vol.Required(CONF_COLUMN_NAME): selector.TextSelector(), vol.Required(CONF_COLUMN_NAME): selector.TextSelector(),
vol.Required(CONF_QUERY): selector.TextSelector(), vol.Required(CONF_QUERY): selector.TextSelector(
selector.TextSelectorConfig(multiline=True)
),
vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.TextSelector(), vol.Optional(CONF_UNIT_OF_MEASUREMENT): selector.TextSelector(),
vol.Optional(CONF_VALUE_TEMPLATE): selector.TemplateSelector(), vol.Optional(CONF_VALUE_TEMPLATE): selector.TemplateSelector(),
} }
@ -165,7 +167,14 @@ class SQLOptionsFlowHandler(config_entries.OptionsFlow):
except ValueError: except ValueError:
errors["query"] = "query_invalid" errors["query"] = "query_invalid"
else: else:
return self.async_create_entry(title="", data=user_input) return self.async_create_entry(
title="",
data={
CONF_NAME: self.entry.title,
**self.entry.options,
**user_input,
},
)
return self.async_show_form( return self.async_show_form(
step_id="init", step_id="init",
@ -180,7 +189,9 @@ class SQLOptionsFlowHandler(config_entries.OptionsFlow):
vol.Required( vol.Required(
CONF_QUERY, CONF_QUERY,
description={"suggested_value": self.entry.options[CONF_QUERY]}, description={"suggested_value": self.entry.options[CONF_QUERY]},
): selector.TextSelector(), ): selector.TextSelector(
selector.TextSelectorConfig(multiline=True)
),
vol.Required( vol.Required(
CONF_COLUMN_NAME, CONF_COLUMN_NAME,
description={ description={

View File

@ -2,7 +2,7 @@
"domain": "totalconnect", "domain": "totalconnect",
"name": "Total Connect", "name": "Total Connect",
"documentation": "https://www.home-assistant.io/integrations/totalconnect", "documentation": "https://www.home-assistant.io/integrations/totalconnect",
"requirements": ["total_connect_client==2022.3"], "requirements": ["total_connect_client==2022.5"],
"dependencies": [], "dependencies": [],
"codeowners": ["@austinmroczek"], "codeowners": ["@austinmroczek"],
"config_flow": true, "config_flow": true,

View File

@ -0,0 +1,79 @@
"""The ukraine_alarm component."""
from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
import aiohttp
from aiohttp import ClientSession
from ukrainealarm.client import Client
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_REGION
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import ALERT_TYPES, DOMAIN, PLATFORMS
_LOGGER = logging.getLogger(__name__)
UPDATE_INTERVAL = timedelta(seconds=10)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Ukraine Alarm as config entry."""
api_key = entry.data[CONF_API_KEY]
region_id = entry.data[CONF_REGION]
websession = async_get_clientsession(hass)
coordinator = UkraineAlarmDataUpdateCoordinator(
hass, websession, api_key, region_id
)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
class UkraineAlarmDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Class to manage fetching Ukraine Alarm API."""
def __init__(
self,
hass: HomeAssistant,
session: ClientSession,
api_key: str,
region_id: str,
) -> None:
"""Initialize."""
self.region_id = region_id
self.ukrainealarm = Client(session, api_key)
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL)
async def _async_update_data(self) -> dict[str, Any]:
"""Update data via library."""
try:
res = await self.ukrainealarm.get_alerts(self.region_id)
except aiohttp.ClientError as error:
raise UpdateFailed(f"Error fetching alerts from API: {error}") from error
current = {alert_type: False for alert_type in ALERT_TYPES}
for alert in res[0]["activeAlerts"]:
current[alert["type"]] = True
return current

View File

@ -0,0 +1,106 @@
"""binary sensors for Ukraine Alarm integration."""
from __future__ import annotations
from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from . import UkraineAlarmDataUpdateCoordinator
from .const import (
ALERT_TYPE_AIR,
ALERT_TYPE_ARTILLERY,
ALERT_TYPE_UNKNOWN,
ALERT_TYPE_URBAN_FIGHTS,
ATTRIBUTION,
DOMAIN,
MANUFACTURER,
)
BINARY_SENSOR_TYPES: tuple[BinarySensorEntityDescription, ...] = (
BinarySensorEntityDescription(
key=ALERT_TYPE_UNKNOWN,
name="Unknown",
device_class=BinarySensorDeviceClass.SAFETY,
),
BinarySensorEntityDescription(
key=ALERT_TYPE_AIR,
name="Air",
device_class=BinarySensorDeviceClass.SAFETY,
icon="mdi:cloud",
),
BinarySensorEntityDescription(
key=ALERT_TYPE_URBAN_FIGHTS,
name="Urban Fights",
device_class=BinarySensorDeviceClass.SAFETY,
icon="mdi:pistol",
),
BinarySensorEntityDescription(
key=ALERT_TYPE_ARTILLERY,
name="Artillery",
device_class=BinarySensorDeviceClass.SAFETY,
icon="mdi:tank",
),
)
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Ukraine Alarm binary sensor entities based on a config entry."""
name = config_entry.data[CONF_NAME]
coordinator = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities(
UkraineAlarmSensor(
name,
config_entry.unique_id,
description,
coordinator,
)
for description in BINARY_SENSOR_TYPES
)
class UkraineAlarmSensor(
CoordinatorEntity[UkraineAlarmDataUpdateCoordinator], BinarySensorEntity
):
"""Class for a Ukraine Alarm binary sensor."""
_attr_attribution = ATTRIBUTION
def __init__(
self,
name,
unique_id,
description: BinarySensorEntityDescription,
coordinator: UkraineAlarmDataUpdateCoordinator,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_name = f"{name} {description.name}"
self._attr_unique_id = f"{unique_id}-{description.key}".lower()
self._attr_device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE,
identifiers={(DOMAIN, unique_id)},
manufacturer=MANUFACTURER,
name=name,
)
@property
def is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""
return self.coordinator.data.get(self.entity_description.key, None)

View File

@ -0,0 +1,154 @@
"""Config flow for Ukraine Alarm."""
from __future__ import annotations
import asyncio
import aiohttp
from ukrainealarm.client import Client
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_API_KEY, CONF_NAME, CONF_REGION
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN
class UkraineAlarmConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Config flow for Ukraine Alarm."""
VERSION = 1
def __init__(self):
"""Initialize a new UkraineAlarmConfigFlow."""
self.api_key = None
self.states = None
self.selected_region = None
async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
errors = {}
if user_input is not None:
websession = async_get_clientsession(self.hass)
try:
regions = await Client(
websession, user_input[CONF_API_KEY]
).get_regions()
except aiohttp.ClientResponseError as ex:
errors["base"] = "invalid_api_key" if ex.status == 401 else "unknown"
except aiohttp.ClientConnectionError:
errors["base"] = "cannot_connect"
except aiohttp.ClientError:
errors["base"] = "unknown"
except asyncio.TimeoutError:
errors["base"] = "timeout"
if not errors and not regions:
errors["base"] = "unknown"
if not errors:
self.api_key = user_input[CONF_API_KEY]
self.states = regions["states"]
return await self.async_step_state()
schema = vol.Schema(
{
vol.Required(CONF_API_KEY): str,
}
)
return self.async_show_form(
step_id="user",
data_schema=schema,
description_placeholders={"api_url": "https://api.ukrainealarm.com/"},
errors=errors,
last_step=False,
)
async def async_step_state(self, user_input=None):
"""Handle user-chosen state."""
return await self._handle_pick_region("state", "district", user_input)
async def async_step_district(self, user_input=None):
"""Handle user-chosen district."""
return await self._handle_pick_region("district", "community", user_input)
async def async_step_community(self, user_input=None):
"""Handle user-chosen community."""
return await self._handle_pick_region("community", None, user_input, True)
async def _handle_pick_region(
self, step_id: str, next_step: str | None, user_input, last_step=False
):
"""Handle picking a (sub)region."""
if self.selected_region:
source = self.selected_region["regionChildIds"]
else:
source = self.states
if user_input is not None:
# Only offer to browse subchildren if picked region wasn't the previously picked one
if (
not self.selected_region
or user_input[CONF_REGION] != self.selected_region["regionId"]
):
self.selected_region = _find(source, user_input[CONF_REGION])
if next_step and self.selected_region["regionChildIds"]:
return await getattr(self, f"async_step_{next_step}")()
return await self._async_finish_flow()
regions = {}
if self.selected_region:
regions[self.selected_region["regionId"]] = self.selected_region[
"regionName"
]
regions.update(_make_regions_object(source))
schema = vol.Schema(
{
vol.Required(CONF_REGION): vol.In(regions),
}
)
return self.async_show_form(
step_id=step_id, data_schema=schema, last_step=last_step
)
async def _async_finish_flow(self):
"""Finish the setup."""
await self.async_set_unique_id(self.selected_region["regionId"])
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=self.selected_region["regionName"],
data={
CONF_API_KEY: self.api_key,
CONF_REGION: self.selected_region["regionId"],
CONF_NAME: self.selected_region["regionName"],
},
)
def _find(regions, region_id):
return next((region for region in regions if region["regionId"] == region_id), None)
def _make_regions_object(regions):
regions_list = []
for region in regions:
regions_list.append(
{
"id": region["regionId"],
"name": region["regionName"],
}
)
regions_list = sorted(regions_list, key=lambda region: region["name"].lower())
regions_object = {}
for region in regions_list:
regions_object[region["id"]] = region["name"]
return regions_object

View File

@ -0,0 +1,19 @@
"""Consts for the Ukraine Alarm."""
from __future__ import annotations
from homeassistant.const import Platform
DOMAIN = "ukraine_alarm"
ATTRIBUTION = "Data provided by Ukraine Alarm"
MANUFACTURER = "Ukraine Alarm"
ALERT_TYPE_UNKNOWN = "UNKNOWN"
ALERT_TYPE_AIR = "AIR"
ALERT_TYPE_ARTILLERY = "ARTILLERY"
ALERT_TYPE_URBAN_FIGHTS = "URBAN_FIGHTS"
ALERT_TYPES = {
ALERT_TYPE_UNKNOWN,
ALERT_TYPE_AIR,
ALERT_TYPE_ARTILLERY,
ALERT_TYPE_URBAN_FIGHTS,
}
PLATFORMS = [Platform.BINARY_SENSOR]

View File

@ -0,0 +1,9 @@
{
"domain": "ukraine_alarm",
"name": "Ukraine Alarm",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/ukraine_alarm",
"requirements": ["ukrainealarm==0.0.1"],
"codeowners": ["@PaulAnnekov"],
"iot_class": "cloud_polling"
}

View File

@ -0,0 +1,39 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]"
},
"error": {
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"unknown": "[%key:common::config_flow::error::unknown%]",
"timeout": "[%key:common::config_flow::error::timeout_connect%]"
},
"step": {
"user": {
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]"
},
"description": "Set up the Ukraine Alarm integration. To generate an API key go to {api_url}"
},
"state": {
"data": {
"region": "Region"
},
"description": "Choose state to monitor"
},
"district": {
"data": {
"region": "[%key:component::ukraine_alarm::config::step::state::data::region%]"
},
"description": "If you want to monitor not only state, choose its specific district"
},
"community": {
"data": {
"region": "[%key:component::ukraine_alarm::config::step::state::data::region%]"
},
"description": "If you want to monitor not only state and district, choose its specific community"
}
}
}
}

View File

@ -0,0 +1,28 @@
{
"config": {
"step": {
"user": {
"description": "Set up the Ukraine Alarm integration. To generate an API key go to {api_url}",
"title": "Ukraine Alarm"
},
"state": {
"data": {
"region": "Region"
},
"description": "Choose state to monitor"
},
"district": {
"data": {
"region": "Region"
},
"description": "If you want to monitor not only state, choose its specific district"
},
"community": {
"data": {
"region": "Region"
},
"description": "If you want to monitor not only state and district, choose its specific community"
}
}
}
}

View File

@ -0,0 +1,28 @@
{
"config": {
"step": {
"user": {
"description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f\u0020\u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438\u0020\u0441 Ukraine Alarm. \u0414\u043b\u044f\u0020\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f\u0020\u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0438\u0442\u0435\u0020\u043d\u0430 {api_url}.",
"title": "Ukraine Alarm"
},
"state": {
"data": {
"region": "\u0420\u0435\u0433\u0438\u043e\u043d"
},
"description": "\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0434\u043b\u044f\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433\u0430"
},
"district": {
"data": {
"region": "\u0420\u0435\u0433\u0438\u043e\u043d"
},
"description": "\u0415\u0441\u043b\u0438\u0020\u0432\u044b\u0020\u0436\u0435\u043b\u0430\u0435\u0442\u0435\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c\u0020\u043d\u0435\u0020\u0442\u043e\u043b\u044c\u043a\u043e\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u002c\u0020\u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u0435\u0451\u0020\u0440\u0430\u0439\u043e\u043d"
},
"community": {
"data": {
"region": "\u0420\u0435\u0433\u0438\u043e\u043d"
},
"description": "\u0415\u0441\u043b\u0438\u0020\u0432\u044b\u0020\u0436\u0435\u043b\u0430\u0435\u0442\u0435\u0020\u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u0442\u044c\u0020\u043d\u0435\u0020\u0442\u043e\u043b\u044c\u043a\u043e\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0438\u0020\u0440\u0430\u0439\u043e\u043d\u002c\u0020\u0432\u044b\u0431\u0435\u0440\u0438\u0442\u0435\u0020\u0435\u0451\u0020\u0433\u0440\u043e\u043c\u0430\u0434\u0443"
}
}
}
}

View File

@ -0,0 +1,28 @@
{
"config": {
"step": {
"user": {
"description": "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0439\u0442\u0435 Home Assistant \u0434\u043b\u044f\u0020\u0456\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0456\u0457\u0020\u0437 Ukraine Alarm. \u0414\u043b\u044f\u0020\u043e\u0442\u0440\u0438\u043c\u0430\u043d\u043d\u044f\u0020\u043a\u043b\u044e\u0447\u0430 API, \u043f\u0435\u0440\u0435\u0439\u0434\u0456\u0442\u044c\u0020\u043d\u0430 {api_url}.",
"title": "Ukraine Alarm"
},
"state": {
"data": {
"region": "\u0420\u0435\u0433\u0456\u043e\u043d"
},
"description": "\u041e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0434\u043b\u044f\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u043d\u0433\u0443"
},
"district": {
"data": {
"region": "\u0420\u0435\u0433\u0456\u043e\u043d"
},
"description": "\u042f\u043a\u0449\u043e\u0020\u0432\u0438\u0020\u0431\u0430\u0436\u0430\u0454\u0442\u0435\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u0442\u0438\u0020\u043d\u0435\u0020\u043b\u0438\u0448\u0435\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u002c\u0020\u043e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u0457\u0457\u0020\u0440\u0430\u0439\u043e\u043d"
},
"community": {
"data": {
"region": "\u0420\u0435\u0433\u0456\u043e\u043d"
},
"description": "\u042f\u043a\u0449\u043e\u0020\u0432\u0438\u0020\u0431\u0430\u0436\u0430\u0454\u0442\u0435\u0020\u043c\u043e\u043d\u0456\u0442\u043e\u0440\u0438\u0442\u0438\u0020\u043d\u0435\u0020\u0442\u0456\u043b\u044c\u043a\u0438\u0020\u043e\u0431\u043b\u0430\u0441\u0442\u044c\u0020\u0442\u0430\u0020\u0440\u0430\u0439\u043e\u043d\u002c\u0020\u043e\u0431\u0435\u0440\u0456\u0442\u044c\u0020\u0457\u0457\u0020\u0433\u0440\u043e\u043c\u0430\u0434\u0443"
}
}
}
}

View File

@ -7,7 +7,7 @@ from .backports.enum import StrEnum
MAJOR_VERSION: Final = 2022 MAJOR_VERSION: Final = 2022
MINOR_VERSION: Final = 5 MINOR_VERSION: Final = 5
PATCH_VERSION: Final = "2" PATCH_VERSION: Final = "3"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0) REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)

View File

@ -366,6 +366,7 @@ FLOWS = {
"twentemilieu", "twentemilieu",
"twilio", "twilio",
"twinkly", "twinkly",
"ukraine_alarm",
"unifi", "unifi",
"unifiprotect", "unifiprotect",
"upb", "upb",

View File

@ -704,7 +704,9 @@ class SelectSelector(Selector):
vol.Required("options"): vol.All(vol.Any([str], [select_option])), vol.Required("options"): vol.All(vol.Any([str], [select_option])),
vol.Optional("multiple", default=False): cv.boolean, vol.Optional("multiple", default=False): cv.boolean,
vol.Optional("custom_value", default=False): cv.boolean, vol.Optional("custom_value", default=False): cv.boolean,
vol.Optional("mode"): vol.Coerce(SelectSelectorMode), vol.Optional("mode"): vol.All(
vol.Coerce(SelectSelectorMode), lambda val: val.value
),
} }
) )
@ -827,7 +829,9 @@ class TextSelector(Selector):
vol.Optional("suffix"): str, vol.Optional("suffix"): str,
# The "type" controls the input field in the browser, the resulting # The "type" controls the input field in the browser, the resulting
# data can be any string so we don't validate it. # data can be any string so we don't validate it.
vol.Optional("type"): vol.Coerce(TextSelectorType), vol.Optional("type"): vol.All(
vol.Coerce(TextSelectorType), lambda val: val.value
),
} }
) )

View File

@ -15,7 +15,7 @@ ciso8601==2.2.0
cryptography==36.0.2 cryptography==36.0.2
fnvhash==0.1.0 fnvhash==0.1.0
hass-nabucasa==0.54.0 hass-nabucasa==0.54.0
home-assistant-frontend==20220504.0 home-assistant-frontend==20220504.1
httpx==0.22.0 httpx==0.22.0
ifaddr==0.1.7 ifaddr==0.1.7
jinja2==3.1.1 jinja2==3.1.1

View File

@ -819,7 +819,7 @@ hole==0.7.0
holidays==0.13 holidays==0.13
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20220504.0 home-assistant-frontend==20220504.1
# homeassistant.components.home_connect # homeassistant.components.home_connect
homeconnect==0.7.0 homeconnect==0.7.0
@ -1287,7 +1287,7 @@ pushover_complete==1.1.1
pvo==0.2.2 pvo==0.2.2
# homeassistant.components.canary # homeassistant.components.canary
py-canary==0.5.1 py-canary==0.5.2
# homeassistant.components.cpuspeed # homeassistant.components.cpuspeed
py-cpuinfo==8.0.0 py-cpuinfo==8.0.0
@ -2316,7 +2316,7 @@ tololib==0.1.0b3
toonapi==0.2.1 toonapi==0.2.1
# homeassistant.components.totalconnect # homeassistant.components.totalconnect
total_connect_client==2022.3 total_connect_client==2022.5
# homeassistant.components.tplink_lte # homeassistant.components.tplink_lte
tp-connected==0.0.4 tp-connected==0.0.4
@ -2342,6 +2342,9 @@ twitchAPI==2.5.2
# homeassistant.components.rainforest_eagle # homeassistant.components.rainforest_eagle
uEagle==0.0.2 uEagle==0.0.2
# homeassistant.components.ukraine_alarm
ukrainealarm==0.0.1
# homeassistant.components.unifiprotect # homeassistant.components.unifiprotect
unifi-discovery==1.1.2 unifi-discovery==1.1.2

View File

@ -580,7 +580,7 @@ hole==0.7.0
holidays==0.13 holidays==0.13
# homeassistant.components.frontend # homeassistant.components.frontend
home-assistant-frontend==20220504.0 home-assistant-frontend==20220504.1
# homeassistant.components.home_connect # homeassistant.components.home_connect
homeconnect==0.7.0 homeconnect==0.7.0
@ -865,7 +865,7 @@ pushbullet.py==0.11.0
pvo==0.2.2 pvo==0.2.2
# homeassistant.components.canary # homeassistant.components.canary
py-canary==0.5.1 py-canary==0.5.2
# homeassistant.components.cpuspeed # homeassistant.components.cpuspeed
py-cpuinfo==8.0.0 py-cpuinfo==8.0.0
@ -1501,7 +1501,7 @@ tololib==0.1.0b3
toonapi==0.2.1 toonapi==0.2.1
# homeassistant.components.totalconnect # homeassistant.components.totalconnect
total_connect_client==2022.3 total_connect_client==2022.5
# homeassistant.components.transmission # homeassistant.components.transmission
transmissionrpc==0.11 transmissionrpc==0.11
@ -1524,6 +1524,9 @@ twitchAPI==2.5.2
# homeassistant.components.rainforest_eagle # homeassistant.components.rainforest_eagle
uEagle==0.0.2 uEagle==0.0.2
# homeassistant.components.ukraine_alarm
ukrainealarm==0.0.1
# homeassistant.components.unifiprotect # homeassistant.components.unifiprotect
unifi-discovery==1.1.2 unifi-discovery==1.1.2

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = homeassistant name = homeassistant
version = 2022.5.2 version = 2022.5.3
author = The Home Assistant Authors author = The Home Assistant Authors
author_email = hello@home-assistant.io author_email = hello@home-assistant.io
license = Apache-2.0 license = Apache-2.0

View File

@ -214,9 +214,62 @@ async def test_options_flow(hass: HomeAssistant) -> None:
assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["type"] == RESULT_TYPE_CREATE_ENTRY
assert result["data"] == { assert result["data"] == {
"name": "Get Value",
"db_url": "sqlite://", "db_url": "sqlite://",
"query": "SELECT 5 as size", "query": "SELECT 5 as size",
"column": "size", "column": "size",
"value_template": None,
"unit_of_measurement": "MiB",
}
async def test_options_flow_name_previously_removed(hass: HomeAssistant) -> None:
"""Test options config flow where the name was missing."""
entry = MockConfigEntry(
domain=DOMAIN,
data={},
options={
"db_url": "sqlite://",
"query": "SELECT 5 as value",
"column": "value",
"unit_of_measurement": "MiB",
"value_template": None,
},
title="Get Value Title",
)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
result = await hass.config_entries.options.async_init(entry.entry_id)
assert result["type"] == RESULT_TYPE_FORM
assert result["step_id"] == "init"
with patch(
"homeassistant.components.sql.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={
"db_url": "sqlite://",
"query": "SELECT 5 as size",
"column": "size",
"unit_of_measurement": "MiB",
},
)
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == 1
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
assert result["data"] == {
"name": "Get Value Title",
"db_url": "sqlite://",
"query": "SELECT 5 as size",
"column": "size",
"value_template": None,
"unit_of_measurement": "MiB", "unit_of_measurement": "MiB",
} }
@ -312,6 +365,8 @@ async def test_options_flow_fails_invalid_query(
assert result4["type"] == RESULT_TYPE_CREATE_ENTRY assert result4["type"] == RESULT_TYPE_CREATE_ENTRY
assert result4["data"] == { assert result4["data"] == {
"name": "Get Value",
"value_template": None,
"db_url": "sqlite://", "db_url": "sqlite://",
"query": "SELECT 5 as size", "query": "SELECT 5 as size",
"column": "size", "column": "size",

View File

@ -0,0 +1 @@
"""Tests for the Ukraine Alarm integration."""

View File

@ -0,0 +1,354 @@
"""Test the Ukraine Alarm config flow."""
import asyncio
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from aiohttp import ClientConnectionError, ClientError, ClientResponseError
import pytest
from homeassistant import config_entries
from homeassistant.components.ukraine_alarm.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM
MOCK_API_KEY = "mock-api-key"
def _region(rid, recurse=0, depth=0):
if depth == 0:
name_prefix = "State"
elif depth == 1:
name_prefix = "District"
else:
name_prefix = "Community"
name = f"{name_prefix} {rid}"
region = {"regionId": rid, "regionName": name, "regionChildIds": []}
if not recurse:
return region
for i in range(1, 4):
region["regionChildIds"].append(_region(f"{rid}.{i}", recurse - 1, depth + 1))
return region
REGIONS = {
"states": [_region(f"{i}", i - 1) for i in range(1, 4)],
}
@pytest.fixture(autouse=True)
def mock_get_regions() -> Generator[None, AsyncMock, None]:
"""Mock the get_regions method."""
with patch(
"homeassistant.components.ukraine_alarm.config_flow.Client.get_regions",
return_value=REGIONS,
) as mock_get:
yield mock_get
async def test_state(hass: HomeAssistant) -> None:
"""Test we can create entry for state."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
with patch(
"homeassistant.components.ukraine_alarm.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"region": "1",
},
)
await hass.async_block_till_done()
assert result3["type"] == RESULT_TYPE_CREATE_ENTRY
assert result3["title"] == "State 1"
assert result3["data"] == {
"api_key": MOCK_API_KEY,
"region": "1",
"name": result3["title"],
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_state_district(hass: HomeAssistant) -> None:
"""Test we can create entry for state + district."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"region": "2",
},
)
assert result3["type"] == RESULT_TYPE_FORM
with patch(
"homeassistant.components.ukraine_alarm.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result4 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"region": "2.2",
},
)
await hass.async_block_till_done()
assert result4["type"] == RESULT_TYPE_CREATE_ENTRY
assert result4["title"] == "District 2.2"
assert result4["data"] == {
"api_key": MOCK_API_KEY,
"region": "2.2",
"name": result4["title"],
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_state_district_pick_region(hass: HomeAssistant) -> None:
"""Test we can create entry for region which has districts."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"region": "2",
},
)
assert result3["type"] == RESULT_TYPE_FORM
with patch(
"homeassistant.components.ukraine_alarm.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result4 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"region": "2",
},
)
await hass.async_block_till_done()
assert result4["type"] == RESULT_TYPE_CREATE_ENTRY
assert result4["title"] == "State 2"
assert result4["data"] == {
"api_key": MOCK_API_KEY,
"region": "2",
"name": result4["title"],
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_state_district_community(hass: HomeAssistant) -> None:
"""Test we can create entry for state + district + community."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"region": "3",
},
)
assert result3["type"] == RESULT_TYPE_FORM
result4 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"region": "3.2",
},
)
assert result4["type"] == RESULT_TYPE_FORM
with patch(
"homeassistant.components.ukraine_alarm.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result5 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"region": "3.2.1",
},
)
await hass.async_block_till_done()
assert result5["type"] == RESULT_TYPE_CREATE_ENTRY
assert result5["title"] == "Community 3.2.1"
assert result5["data"] == {
"api_key": MOCK_API_KEY,
"region": "3.2.1",
"name": result5["title"],
}
assert len(mock_setup_entry.mock_calls) == 1
async def test_invalid_api(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None:
"""Test we can create entry for just region."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
mock_get_regions.side_effect = ClientResponseError(None, None, status=401)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "invalid_api_key"}
async def test_server_error(hass: HomeAssistant, mock_get_regions) -> None:
"""Test we can create entry for just region."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
mock_get_regions.side_effect = ClientResponseError(None, None, status=500)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "unknown"}
async def test_cannot_connect(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None:
"""Test we can create entry for just region."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
mock_get_regions.side_effect = ClientConnectionError
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "cannot_connect"}
async def test_unknown_client_error(
hass: HomeAssistant, mock_get_regions: AsyncMock
) -> None:
"""Test we can create entry for just region."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
mock_get_regions.side_effect = ClientError
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "unknown"}
async def test_timeout_error(hass: HomeAssistant, mock_get_regions: AsyncMock) -> None:
"""Test we can create entry for just region."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
mock_get_regions.side_effect = asyncio.TimeoutError
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "timeout"}
async def test_no_regions_returned(
hass: HomeAssistant, mock_get_regions: AsyncMock
) -> None:
"""Test we can create entry for just region."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == RESULT_TYPE_FORM
mock_get_regions.return_value = {}
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
"api_key": MOCK_API_KEY,
},
)
assert result2["type"] == RESULT_TYPE_FORM
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": "unknown"}

View File

@ -351,7 +351,7 @@ def test_object_selector_schema(schema, valid_selections, invalid_selections):
( (
({}, ("abc123",), (None,)), ({}, ("abc123",), (None,)),
({"multiline": True}, (), ()), ({"multiline": True}, (), ()),
({"multiline": False}, (), ()), ({"multiline": False, "type": "email"}, (), ()),
), ),
) )
def test_text_selector_schema(schema, valid_selections, invalid_selections): def test_text_selector_schema(schema, valid_selections, invalid_selections):
@ -402,7 +402,7 @@ def test_text_selector_schema(schema, valid_selections, invalid_selections):
(0, None, ["red"]), (0, None, ["red"]),
), ),
( (
{"options": [], "custom_value": True, "multiple": True}, {"options": [], "custom_value": True, "multiple": True, "mode": "list"},
(["red"], ["green", "blue"], []), (["red"], ["green", "blue"], []),
(0, None, "red"), (0, None, "red"),
), ),