Merge pull request #33030 from home-assistant/rc

0.107.2
This commit is contained in:
Paulus Schoutsen 2020-03-19 22:51:13 -07:00 committed by GitHub
commit ac2310e7f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 174 additions and 86 deletions

View File

@ -21,6 +21,10 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .axis_base import AxisEntityBase
from .const import DOMAIN as AXIS_DOMAIN
AXIS_IMAGE = "http://{host}:{port}/axis-cgi/jpg/image.cgi"
AXIS_VIDEO = "http://{host}:{port}/axis-cgi/mjpg/video.cgi"
AXIS_STREAM = "rtsp://{user}:{password}@{host}/axis-media/media.amp?videocodec=h264"
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Axis camera video stream."""
@ -32,13 +36,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
CONF_NAME: config_entry.data[CONF_NAME],
CONF_USERNAME: config_entry.data[CONF_USERNAME],
CONF_PASSWORD: config_entry.data[CONF_PASSWORD],
CONF_MJPEG_URL: (
f"http://{config_entry.data[CONF_HOST]}"
f":{config_entry.data[CONF_PORT]}/axis-cgi/mjpg/video.cgi"
CONF_MJPEG_URL: AXIS_VIDEO.format(
host=config_entry.data[CONF_HOST], port=config_entry.data[CONF_PORT],
),
CONF_STILL_IMAGE_URL: (
f"http://{config_entry.data[CONF_HOST]}"
f":{config_entry.data[CONF_PORT]}/axis-cgi/jpg/image.cgi"
CONF_STILL_IMAGE_URL: AXIS_IMAGE.format(
host=config_entry.data[CONF_HOST], port=config_entry.data[CONF_PORT],
),
CONF_AUTHENTICATION: HTTP_DIGEST_AUTHENTICATION,
}
@ -70,19 +72,17 @@ class AxisCamera(AxisEntityBase, MjpegCamera):
async def stream_source(self):
"""Return the stream source."""
return (
f"rtsp://{self.device.config_entry.data[CONF_USERNAME]}´"
f":{self.device.config_entry.data[CONF_PASSWORD]}"
f"@{self.device.host}/axis-media/media.amp?videocodec=h264"
return AXIS_STREAM.format(
user=self.device.config_entry.data[CONF_USERNAME],
password=self.device.config_entry.data[CONF_PASSWORD],
host=self.device.host,
)
def _new_address(self):
"""Set new device address for video stream."""
port = self.device.config_entry.data[CONF_PORT]
self._mjpeg_url = (f"http://{self.device.host}:{port}/axis-cgi/mjpg/video.cgi",)
self._still_image_url = (
f"http://{self.device.host}:{port}/axis-cgi/jpg/image.cgi"
)
self._mjpeg_url = AXIS_VIDEO.format(host=self.device.host, port=port)
self._still_image_url = AXIS_IMAGE.format(host=self.device.host, port=port)
@property
def unique_id(self):

View File

@ -141,7 +141,7 @@ async def async_request_stream(hass, entity_id, fmt):
source,
fmt=fmt,
keepalive=camera_prefs.preload_stream,
options=camera.options,
options=camera.stream_options,
)

View File

@ -3,7 +3,7 @@
"name": "HomeKit Controller",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
"requirements": ["aiohomekit[IP]==0.2.29.1"],
"requirements": ["aiohomekit[IP]==0.2.29.2"],
"dependencies": [],
"zeroconf": ["_hap._tcp.local."],
"codeowners": ["@Jc2k"]

View File

@ -25,6 +25,7 @@ from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.service import verify_domain_control
from .const import (
CONF_ZONE_RUN_TIME,
DATA_CLIENT,
DATA_PROGRAMS,
DATA_PROVISION_SETTINGS,
@ -33,6 +34,8 @@ from .const import (
DATA_ZONES,
DATA_ZONES_DETAILS,
DEFAULT_PORT,
DEFAULT_SCAN_INTERVAL,
DEFAULT_ZONE_RUN,
DOMAIN,
PROGRAM_UPDATE_TOPIC,
SENSOR_UPDATE_TOPIC,
@ -41,19 +44,14 @@ from .const import (
_LOGGER = logging.getLogger(__name__)
DATA_LISTENER = "listener"
CONF_CONTROLLERS = "controllers"
CONF_PROGRAM_ID = "program_id"
CONF_SECONDS = "seconds"
CONF_ZONE_ID = "zone_id"
CONF_ZONE_RUN_TIME = "zone_run_time"
DEFAULT_ATTRIBUTION = "Data provided by Green Electronics LLC"
DEFAULT_ICON = "mdi:water"
DEFAULT_SCAN_INTERVAL = timedelta(seconds=60)
DEFAULT_SSL = True
DEFAULT_ZONE_RUN = 60 * 10
SERVICE_ALTER_PROGRAM = vol.Schema({vol.Required(CONF_PROGRAM_ID): cv.positive_int})
@ -109,7 +107,6 @@ async def async_setup(hass, config):
"""Set up the RainMachine component."""
hass.data[DOMAIN] = {}
hass.data[DOMAIN][DATA_CLIENT] = {}
hass.data[DOMAIN][DATA_LISTENER] = {}
if DOMAIN not in config:
return True
@ -143,7 +140,7 @@ async def async_setup_entry(hass, config_entry):
config_entry.data[CONF_IP_ADDRESS],
config_entry.data[CONF_PASSWORD],
port=config_entry.data[CONF_PORT],
ssl=config_entry.data[CONF_SSL],
ssl=config_entry.data.get(CONF_SSL, DEFAULT_SSL),
)
except RainMachineError as err:
_LOGGER.error("An error occurred: %s", err)
@ -156,8 +153,10 @@ async def async_setup_entry(hass, config_entry):
rainmachine = RainMachine(
hass,
controller,
config_entry.data[CONF_ZONE_RUN_TIME],
config_entry.data[CONF_SCAN_INTERVAL],
config_entry.data.get(CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN),
config_entry.data.get(
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL.total_seconds()
),
)
# Update the data object, which at this point (prior to any sensors registering
@ -260,9 +259,6 @@ async def async_unload_entry(hass, config_entry):
"""Unload an OpenUV config entry."""
hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id)
remove_listener = hass.data[DOMAIN][DATA_LISTENER].pop(config_entry.entry_id)
remove_listener()
tasks = [
hass.config_entries.async_forward_entry_unload(config_entry, component)
for component in ("binary_sensor", "sensor", "switch")

View File

@ -4,10 +4,22 @@ from regenmaschine.errors import RainMachineError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_IP_ADDRESS, CONF_PASSWORD, CONF_PORT
from homeassistant.const import (
CONF_IP_ADDRESS,
CONF_PASSWORD,
CONF_PORT,
CONF_SCAN_INTERVAL,
CONF_SSL,
)
from homeassistant.helpers import aiohttp_client
from .const import DEFAULT_PORT, DOMAIN # pylint: disable=unused-import
from .const import ( # pylint: disable=unused-import
CONF_ZONE_RUN_TIME,
DEFAULT_PORT,
DEFAULT_SCAN_INTERVAL,
DEFAULT_ZONE_RUN,
DOMAIN,
)
class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
@ -53,8 +65,8 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
user_input[CONF_IP_ADDRESS],
user_input[CONF_PASSWORD],
websession,
port=user_input.get(CONF_PORT, DEFAULT_PORT),
ssl=True,
port=user_input[CONF_PORT],
ssl=user_input.get(CONF_SSL, True),
)
except RainMachineError:
return await self._show_form({CONF_PASSWORD: "invalid_credentials"})
@ -63,5 +75,17 @@ class RainMachineFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
# access token without using the IP address and password, so we have to
# store it:
return self.async_create_entry(
title=user_input[CONF_IP_ADDRESS], data=user_input
title=user_input[CONF_IP_ADDRESS],
data={
CONF_IP_ADDRESS: user_input[CONF_IP_ADDRESS],
CONF_PASSWORD: user_input[CONF_PASSWORD],
CONF_PORT: user_input[CONF_PORT],
CONF_SSL: user_input.get(CONF_SSL, True),
CONF_SCAN_INTERVAL: user_input.get(
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL.total_seconds()
),
CONF_ZONE_RUN_TIME: user_input.get(
CONF_ZONE_RUN_TIME, DEFAULT_ZONE_RUN
),
},
)

View File

@ -1,6 +1,10 @@
"""Define constants for the SimpliSafe component."""
from datetime import timedelta
DOMAIN = "rainmachine"
CONF_ZONE_RUN_TIME = "zone_run_time"
DATA_CLIENT = "client"
DATA_PROGRAMS = "programs"
DATA_PROVISION_SETTINGS = "provision.settings"
@ -10,6 +14,8 @@ DATA_ZONES = "zones"
DATA_ZONES_DETAILS = "zones_details"
DEFAULT_PORT = 8080
DEFAULT_SCAN_INTERVAL = timedelta(seconds=60)
DEFAULT_ZONE_RUN = 60 * 10
PROGRAM_UPDATE_TOPIC = f"{DOMAIN}_program_update"
SENSOR_UPDATE_TOPIC = f"{DOMAIN}_data_update"

View File

@ -3,6 +3,7 @@
"name": "Sighthound",
"documentation": "https://www.home-assistant.io/integrations/sighthound",
"requirements": [
"pillow==7.0.0",
"simplehound==0.3"
],
"dependencies": [],

View File

@ -3,7 +3,7 @@
"name": "SimpliSafe",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/simplisafe",
"requirements": ["simplisafe-python==9.0.2"],
"requirements": ["simplisafe-python==9.0.3"],
"dependencies": [],
"codeowners": ["@bachya"]
}

View File

@ -27,7 +27,7 @@ DOMAIN = "somfy"
CONF_CLIENT_ID = "client_id"
CONF_CLIENT_SECRET = "client_secret"
CONF_OPTIMISTIC = "optimisitic"
CONF_OPTIMISTIC = "optimistic"
SOMFY_AUTH_CALLBACK_PATH = "/auth/somfy/callback"
SOMFY_AUTH_START = "/auth/somfy"
@ -36,8 +36,8 @@ CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
{
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
vol.Inclusive(CONF_CLIENT_ID, "oauth"): cv.string,
vol.Inclusive(CONF_CLIENT_SECRET, "oauth"): cv.string,
vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
}
)
@ -51,23 +51,21 @@ SOMFY_COMPONENTS = ["cover", "switch"]
async def async_setup(hass, config):
"""Set up the Somfy component."""
hass.data[DOMAIN] = {}
domain_config = config.get(DOMAIN, {})
hass.data[DOMAIN][CONF_OPTIMISTIC] = domain_config.get(CONF_OPTIMISTIC, False)
if DOMAIN not in config:
return True
hass.data[DOMAIN][CONF_OPTIMISTIC] = config[DOMAIN][CONF_OPTIMISTIC]
config_flow.SomfyFlowHandler.async_register_implementation(
hass,
config_entry_oauth2_flow.LocalOAuth2Implementation(
if CONF_CLIENT_ID in domain_config:
config_flow.SomfyFlowHandler.async_register_implementation(
hass,
DOMAIN,
config[DOMAIN][CONF_CLIENT_ID],
config[DOMAIN][CONF_CLIENT_SECRET],
"https://accounts.somfy.com/oauth/oauth/v2/auth",
"https://accounts.somfy.com/oauth/oauth/v2/token",
),
)
config_entry_oauth2_flow.LocalOAuth2Implementation(
hass,
DOMAIN,
config[DOMAIN][CONF_CLIENT_ID],
config[DOMAIN][CONF_CLIENT_SECRET],
"https://accounts.somfy.com/oauth/oauth/v2/auth",
"https://accounts.somfy.com/oauth/oauth/v2/token",
),
)
return True

View File

@ -2,7 +2,7 @@
"domain": "velbus",
"name": "Velbus",
"documentation": "https://www.home-assistant.io/integrations/velbus",
"requirements": ["python-velbus==2.0.42"],
"requirements": ["python-velbus==2.0.43"],
"config_flow": true,
"dependencies": [],
"codeowners": ["@Cereal2nd", "@brefra"]

View File

@ -89,9 +89,19 @@ async def async_setup_entry(hass, config_entry):
Will automatically load components to support devices found on the network.
"""
hass.data[DATA_ZHA] = hass.data.get(DATA_ZHA, {})
hass.data[DATA_ZHA][DATA_ZHA_DISPATCHERS] = []
hass.data[DATA_ZHA][DATA_ZHA_PLATFORM_LOADED] = asyncio.Event()
zha_data = hass.data.setdefault(DATA_ZHA, {})
config = zha_data.get(DATA_ZHA_CONFIG, {})
if config.get(CONF_ENABLE_QUIRKS, True):
# needs to be done here so that the ZHA module is finished loading
# before zhaquirks is imported
import zhaquirks # noqa: F401 pylint: disable=unused-import, import-outside-toplevel, import-error
zha_gateway = ZHAGateway(hass, config, config_entry)
await zha_gateway.async_initialize()
zha_data[DATA_ZHA_DISPATCHERS] = []
zha_data[DATA_ZHA_PLATFORM_LOADED] = asyncio.Event()
platforms = []
for component in COMPONENTS:
platforms.append(
@ -102,20 +112,10 @@ async def async_setup_entry(hass, config_entry):
async def _platforms_loaded():
await asyncio.gather(*platforms)
hass.data[DATA_ZHA][DATA_ZHA_PLATFORM_LOADED].set()
zha_data[DATA_ZHA_PLATFORM_LOADED].set()
hass.async_create_task(_platforms_loaded())
config = hass.data[DATA_ZHA].get(DATA_ZHA_CONFIG, {})
if config.get(CONF_ENABLE_QUIRKS, True):
# needs to be done here so that the ZHA module is finished loading
# before zhaquirks is imported
import zhaquirks # noqa: F401 pylint: disable=unused-import, import-outside-toplevel, import-error
zha_gateway = ZHAGateway(hass, config, config_entry)
await zha_gateway.async_initialize()
device_registry = await hass.helpers.device_registry.async_get_registry()
device_registry.async_get_or_create(
config_entry_id=config_entry.entry_id,
@ -130,8 +130,8 @@ async def async_setup_entry(hass, config_entry):
async def async_zha_shutdown(event):
"""Handle shutdown tasks."""
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].shutdown()
await hass.data[DATA_ZHA][DATA_ZHA_GATEWAY].async_update_device_storage()
await zha_data[DATA_ZHA_GATEWAY].shutdown()
await zha_data[DATA_ZHA_GATEWAY].async_update_device_storage()
hass.bus.async_listen_once(ha_const.EVENT_HOMEASSISTANT_STOP, async_zha_shutdown)
hass.async_create_task(zha_gateway.async_load_devices())

View File

@ -85,11 +85,11 @@ class ZigbeeChannel(LogMixin):
self, cluster: zha_typing.ZigpyClusterType, ch_pool: zha_typing.ChannelPoolType
) -> None:
"""Initialize ZigbeeChannel."""
self._channel_name = cluster.ep_attribute
self._generic_id = f"channel_0x{cluster.cluster_id:04x}"
self._channel_name = getattr(cluster, "ep_attribute", self._generic_id)
if self.CHANNEL_NAME:
self._channel_name = self.CHANNEL_NAME
self._ch_pool = ch_pool
self._generic_id = f"channel_0x{cluster.cluster_id:04x}"
self._cluster = cluster
self._id = f"{ch_pool.id}:0x{cluster.cluster_id:04x}"
unique_id = ch_pool.unique_id.replace("-", ":")

View File

@ -7,10 +7,12 @@ import logging
import os
import traceback
from serial import SerialException
import zigpy.device as zigpy_dev
from homeassistant.components.system_log import LogEntry, _figure_out_source
from homeassistant.core import callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.device_registry import (
CONNECTION_ZIGBEE,
async_get_registry as get_dev_reg,
@ -98,7 +100,6 @@ class ZHAGateway:
self.ha_entity_registry = None
self.application_controller = None
self.radio_description = None
hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] = self
self._log_levels = {
DEBUG_LEVEL_ORIGINAL: async_capture_log_levels(),
DEBUG_LEVEL_CURRENT: async_capture_log_levels(),
@ -122,7 +123,11 @@ class ZHAGateway:
radio_details = RADIO_TYPES[radio_type]
radio = radio_details[ZHA_GW_RADIO]()
self.radio_description = radio_details[ZHA_GW_RADIO_DESCRIPTION]
await radio.connect(usb_path, baudrate)
try:
await radio.connect(usb_path, baudrate)
except (SerialException, OSError) as exception:
_LOGGER.error("Couldn't open serial port for ZHA: %s", str(exception))
raise ConfigEntryNotReady
if CONF_DATABASE in self._config:
database = self._config[CONF_DATABASE]
@ -133,7 +138,22 @@ class ZHAGateway:
apply_application_controller_patch(self)
self.application_controller.add_listener(self)
self.application_controller.groups.add_listener(self)
await self.application_controller.startup(auto_form=True)
try:
res = await self.application_controller.startup(auto_form=True)
if res is False:
await self.application_controller.shutdown()
raise ConfigEntryNotReady
except asyncio.TimeoutError as exception:
_LOGGER.error(
"Couldn't start %s coordinator",
radio_details[ZHA_GW_RADIO_DESCRIPTION],
exc_info=exception,
)
radio.close()
raise ConfigEntryNotReady from exception
self._hass.data[DATA_ZHA][DATA_ZHA_GATEWAY] = self
self._hass.data[DATA_ZHA][DATA_ZHA_BRIDGE_ID] = str(
self.application_controller.ieee
)

View File

@ -565,9 +565,23 @@ def _log_pkg_error(package: str, component: str, config: Dict, message: str) ->
def _identify_config_schema(module: ModuleType) -> Tuple[Optional[str], Optional[Dict]]:
"""Extract the schema and identify list or dict based."""
try:
schema = module.CONFIG_SCHEMA.schema[module.DOMAIN] # type: ignore
except (AttributeError, KeyError):
key = next(k for k in module.CONFIG_SCHEMA.schema if k == module.DOMAIN) # type: ignore
except (AttributeError, StopIteration):
return None, None
schema = module.CONFIG_SCHEMA.schema[key] # type: ignore
if hasattr(key, "default"):
default_value = schema(key.default())
if isinstance(default_value, dict):
return "dict", schema
if isinstance(default_value, list):
return "list", schema
return None, None
t_schema = str(schema)
if t_schema.startswith("{") or "schema_with_slug_keys" in t_schema:
return ("dict", schema)

View File

@ -1,7 +1,7 @@
"""Constants used by Home Assistant components."""
MAJOR_VERSION = 0
MINOR_VERSION = 107
PATCH_VERSION = "1"
PATCH_VERSION = "2"
__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__ = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER = (3, 7, 0)

View File

@ -163,7 +163,7 @@ aioftp==0.12.0
aioharmony==0.1.13
# homeassistant.components.homekit_controller
aiohomekit[IP]==0.2.29.1
aiohomekit[IP]==0.2.29.2
# homeassistant.components.emulated_hue
# homeassistant.components.http
@ -1017,6 +1017,7 @@ pilight==0.1.1
# homeassistant.components.proxy
# homeassistant.components.qrcode
# homeassistant.components.seven_segments
# homeassistant.components.sighthound
# homeassistant.components.tensorflow
pillow==7.0.0
@ -1665,7 +1666,7 @@ python-telnet-vlc==1.0.4
python-twitch-client==0.6.0
# homeassistant.components.velbus
python-velbus==2.0.42
python-velbus==2.0.43
# homeassistant.components.vlc
python-vlc==1.1.2
@ -1855,7 +1856,7 @@ simplehound==0.3
simplepush==1.1.4
# homeassistant.components.simplisafe
simplisafe-python==9.0.2
simplisafe-python==9.0.3
# homeassistant.components.sisyphus
sisyphus-control==2.2.1

View File

@ -62,7 +62,7 @@ aiobotocore==0.11.1
aioesphomeapi==2.6.1
# homeassistant.components.homekit_controller
aiohomekit[IP]==0.2.29.1
aiohomekit[IP]==0.2.29.2
# homeassistant.components.emulated_hue
# homeassistant.components.http
@ -365,6 +365,14 @@ pexpect==4.6.0
# homeassistant.components.pilight
pilight==0.1.1
# homeassistant.components.doods
# homeassistant.components.proxy
# homeassistant.components.qrcode
# homeassistant.components.seven_segments
# homeassistant.components.sighthound
# homeassistant.components.tensorflow
pillow==7.0.0
# homeassistant.components.plex
plexapi==3.3.0
@ -587,7 +595,7 @@ python-nest==4.1.0
python-twitch-client==0.6.0
# homeassistant.components.velbus
python-velbus==2.0.42
python-velbus==2.0.43
# homeassistant.components.awair
python_awair==0.0.4
@ -641,7 +649,7 @@ sentry-sdk==0.13.5
simplehound==0.3
# homeassistant.components.simplisafe
simplisafe-python==9.0.2
simplisafe-python==9.0.3
# homeassistant.components.sleepiq
sleepyq==0.7

View File

@ -4,7 +4,7 @@ from unittest.mock import patch
from regenmaschine.errors import RainMachineError
from homeassistant import data_entry_flow
from homeassistant.components.rainmachine import DOMAIN, config_flow
from homeassistant.components.rainmachine import CONF_ZONE_RUN_TIME, DOMAIN, config_flow
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import (
CONF_IP_ADDRESS,
@ -98,6 +98,7 @@ async def test_step_import(hass):
CONF_PORT: 8080,
CONF_SSL: True,
CONF_SCAN_INTERVAL: 60,
CONF_ZONE_RUN_TIME: 600,
}
@ -129,4 +130,5 @@ async def test_step_user(hass):
CONF_PORT: 8080,
CONF_SSL: True,
CONF_SCAN_INTERVAL: 60,
CONF_ZONE_RUN_TIME: 600,
}

View File

@ -123,7 +123,7 @@ async def test_tracked_devices(hass):
devices_response=[DEVICE_1, DEVICE_2],
known_wireless_clients=(CLIENT_4["mac"],),
)
assert len(hass.states.async_entity_ids("device_tracker")) == 5
assert len(hass.states.async_entity_ids("device_tracker")) == 6
client_1 = hass.states.get("device_tracker.client_1")
assert client_1 is not None
@ -349,7 +349,7 @@ async def test_option_ssid_filter(hass):
controller = await setup_unifi_integration(
hass, options={CONF_SSID_FILTER: ["ssid"]}, clients_response=[CLIENT_3],
)
assert len(hass.states.async_entity_ids("device_tracker")) == 0
assert len(hass.states.async_entity_ids("device_tracker")) == 1
# SSID filter active
client_3 = hass.states.get("device_tracker.client_3")

View File

@ -10,6 +10,7 @@ from unittest.mock import Mock
import asynctest
from asynctest import CoroutineMock, patch
import pytest
import voluptuous as vol
from voluptuous import Invalid, MultipleInvalid
import yaml
@ -989,3 +990,20 @@ async def test_component_config_exceptions(hass, caplog):
"Unknown error validating config for test_platform platform for test_domain component with PLATFORM_SCHEMA"
in caplog.text
)
@pytest.mark.parametrize(
"domain, schema, expected",
[
("zone", vol.Schema({vol.Optional("zone", default=[]): list}), "list"),
("zone", vol.Schema({vol.Optional("zone", default=dict): dict}), "dict"),
],
)
def test_identify_config_schema(domain, schema, expected):
"""Test identify config schema."""
assert (
config_util._identify_config_schema(Mock(DOMAIN=domain, CONFIG_SCHEMA=schema))[
0
]
== expected
)