diff --git a/homeassistant/components/axis/camera.py b/homeassistant/components/axis/camera.py index 3cf84ce2288..c914319aa42 100644 --- a/homeassistant/components/axis/camera.py +++ b/homeassistant/components/axis/camera.py @@ -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): diff --git a/homeassistant/components/camera/__init__.py b/homeassistant/components/camera/__init__.py index 5d9bc99f945..6bbf30b000e 100644 --- a/homeassistant/components/camera/__init__.py +++ b/homeassistant/components/camera/__init__.py @@ -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, ) diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index a73d68227c7..b09cc198006 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -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"] diff --git a/homeassistant/components/rainmachine/__init__.py b/homeassistant/components/rainmachine/__init__.py index 4844a9e68c8..d2c0281fa80 100644 --- a/homeassistant/components/rainmachine/__init__.py +++ b/homeassistant/components/rainmachine/__init__.py @@ -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") diff --git a/homeassistant/components/rainmachine/config_flow.py b/homeassistant/components/rainmachine/config_flow.py index ffa46cc2c15..dc1ee16d05f 100644 --- a/homeassistant/components/rainmachine/config_flow.py +++ b/homeassistant/components/rainmachine/config_flow.py @@ -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 + ), + }, ) diff --git a/homeassistant/components/rainmachine/const.py b/homeassistant/components/rainmachine/const.py index 855ff5d5df5..a88573476ba 100644 --- a/homeassistant/components/rainmachine/const.py +++ b/homeassistant/components/rainmachine/const.py @@ -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" diff --git a/homeassistant/components/sighthound/manifest.json b/homeassistant/components/sighthound/manifest.json index 737aa01c21f..a891d807f57 100644 --- a/homeassistant/components/sighthound/manifest.json +++ b/homeassistant/components/sighthound/manifest.json @@ -3,6 +3,7 @@ "name": "Sighthound", "documentation": "https://www.home-assistant.io/integrations/sighthound", "requirements": [ + "pillow==7.0.0", "simplehound==0.3" ], "dependencies": [], diff --git a/homeassistant/components/simplisafe/manifest.json b/homeassistant/components/simplisafe/manifest.json index b5f89a65fea..0fdcdcfcf5e 100644 --- a/homeassistant/components/simplisafe/manifest.json +++ b/homeassistant/components/simplisafe/manifest.json @@ -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"] } diff --git a/homeassistant/components/somfy/__init__.py b/homeassistant/components/somfy/__init__.py index 1b2722882e6..619e5a72602 100644 --- a/homeassistant/components/somfy/__init__.py +++ b/homeassistant/components/somfy/__init__.py @@ -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 diff --git a/homeassistant/components/velbus/manifest.json b/homeassistant/components/velbus/manifest.json index 4179c3e89ba..fe3aee9a4cd 100644 --- a/homeassistant/components/velbus/manifest.json +++ b/homeassistant/components/velbus/manifest.json @@ -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"] diff --git a/homeassistant/components/zha/__init__.py b/homeassistant/components/zha/__init__.py index 0d4ceed829b..ac5648e097b 100644 --- a/homeassistant/components/zha/__init__.py +++ b/homeassistant/components/zha/__init__.py @@ -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()) diff --git a/homeassistant/components/zha/core/channels/base.py b/homeassistant/components/zha/core/channels/base.py index dca0bbe09f3..dfe564ec2c1 100644 --- a/homeassistant/components/zha/core/channels/base.py +++ b/homeassistant/components/zha/core/channels/base.py @@ -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("-", ":") diff --git a/homeassistant/components/zha/core/gateway.py b/homeassistant/components/zha/core/gateway.py index 6c0681d9eca..1ad10710c60 100644 --- a/homeassistant/components/zha/core/gateway.py +++ b/homeassistant/components/zha/core/gateway.py @@ -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 ) diff --git a/homeassistant/config.py b/homeassistant/config.py index abb8511cab0..27aff8ca36b 100644 --- a/homeassistant/config.py +++ b/homeassistant/config.py @@ -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) diff --git a/homeassistant/const.py b/homeassistant/const.py index 44a9c298368..3d4e380439c 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -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) diff --git a/requirements_all.txt b/requirements_all.txt index abfd541140f..11d241bbc9f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 26bd4c22331..f21ef66187e 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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 diff --git a/tests/components/rainmachine/test_config_flow.py b/tests/components/rainmachine/test_config_flow.py index 38dafdda986..fca0f624a29 100644 --- a/tests/components/rainmachine/test_config_flow.py +++ b/tests/components/rainmachine/test_config_flow.py @@ -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, } diff --git a/tests/components/unifi/test_device_tracker.py b/tests/components/unifi/test_device_tracker.py index 56f82dfd0f1..079bbd7d751 100644 --- a/tests/components/unifi/test_device_tracker.py +++ b/tests/components/unifi/test_device_tracker.py @@ -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") diff --git a/tests/test_config.py b/tests/test_config.py index fc5ec43093b..43f1263e581 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -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 + )