From 05157b526d3029abb8be8b0f0ce3c0aa31b43d5c Mon Sep 17 00:00:00 2001 From: Gerardo Castillo <32140109+altersis@users.noreply.github.com> Date: Wed, 17 Mar 2021 17:45:54 +0000 Subject: [PATCH 1/8] first commit to repo --- .devcontainer/devcontainer.json | 2 +- CODEOWNERS | 1 + .../components/leviosa_shades/__init__.py | 44 +++++ .../components/leviosa_shades/config_flow.py | 186 ++++++++++++++++++ .../components/leviosa_shades/const.py | 37 ++++ .../components/leviosa_shades/cover.py | 184 +++++++++++++++++ .../components/leviosa_shades/manifest.json | 12 ++ .../components/leviosa_shades/services.yaml | 17 ++ .../components/leviosa_shades/strings.json | 26 +++ .../leviosa_shades/translations/en.json | 42 ++++ homeassistant/generated/config_flows.py | 1 + requirements_all.txt | 4 + requirements_test_all.txt | 4 + tests/components/leviosa_shades/__init__.py | 1 + .../leviosa_shades/test_config_flow.py | 94 +++++++++ 15 files changed, 654 insertions(+), 1 deletion(-) create mode 100644 homeassistant/components/leviosa_shades/__init__.py create mode 100644 homeassistant/components/leviosa_shades/config_flow.py create mode 100644 homeassistant/components/leviosa_shades/const.py create mode 100644 homeassistant/components/leviosa_shades/cover.py create mode 100644 homeassistant/components/leviosa_shades/manifest.json create mode 100644 homeassistant/components/leviosa_shades/services.yaml create mode 100644 homeassistant/components/leviosa_shades/strings.json create mode 100644 homeassistant/components/leviosa_shades/translations/en.json create mode 100644 tests/components/leviosa_shades/__init__.py create mode 100644 tests/components/leviosa_shades/test_config_flow.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index efcc038074811..fbec0bfdf3d72 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,7 +6,7 @@ "postStartCommand": "script/bootstrap", "containerEnv": { "DEVCONTAINER": "1" }, "appPort": 8123, - "runArgs": ["-e", "GIT_EDITOR=code --wait"], + "runArgs": ["-e", "GIT_EDITOR=code --wait", "--network=host"], "extensions": [ "ms-python.vscode-pylance", "visualstudioexptteam.vscodeintellicode", diff --git a/CODEOWNERS b/CODEOWNERS index 263e5337c588b..d1177b8efccd5 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -251,6 +251,7 @@ homeassistant/components/kulersky/* @emlove homeassistant/components/lametric/* @robbiet480 homeassistant/components/launch_library/* @ludeeus homeassistant/components/lcn/* @alengwenus +homeassistant/components/leviosa_shades/* @altersis homeassistant/components/life360/* @pnbruckner homeassistant/components/linux_battery/* @fabaff homeassistant/components/litejet/* @joncar diff --git a/homeassistant/components/leviosa_shades/__init__.py b/homeassistant/components/leviosa_shades/__init__.py new file mode 100644 index 0000000000000..30beb9613434b --- /dev/null +++ b/homeassistant/components/leviosa_shades/__init__.py @@ -0,0 +1,44 @@ +"""The Leviosa shades Zone integration.""" +import asyncio +import logging + +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +_LOGGER = logging.getLogger(__name__) + +PLATFORMS = ["cover"] + + +async def async_setup(hass: HomeAssistant, config: dict): + """Set up the Leviosa shades Zone component.""" + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): + """Set up Leviosa shades Zone from a config entry.""" + + for component in PLATFORMS: + hass.async_create_task( + hass.config_entries.async_forward_entry_setup(entry, component) + ) + + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): + """Unload a config entry.""" + unload_ok = all( + await asyncio.gather( + *[ + hass.config_entries.async_forward_entry_unload(entry, component) + for component in PLATFORMS + ] + ) + ) + # if unload_ok: + # _LOGGER.debug("data: %s", entry.data) + # hass.data[DOMAIN].pop(entry.entry_id) + # # hass.data["cover"].pop(entry.entry_id) + + return unload_ok diff --git a/homeassistant/components/leviosa_shades/config_flow.py b/homeassistant/components/leviosa_shades/config_flow.py new file mode 100644 index 0000000000000..dd79e0eed71f7 --- /dev/null +++ b/homeassistant/components/leviosa_shades/config_flow.py @@ -0,0 +1,186 @@ +"""Config flow for Leviosa shades Zone.""" +import logging + +from aioleviosa import LeviosaZoneHub, discover_leviosa_zones +import voluptuous as vol + +from homeassistant import config_entries, core, exceptions +from homeassistant.const import CONF_HOST, CONF_NAME +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +from .const import ( + BLIND_GROUPS, + DEVICE_FW_V, + DEVICE_MAC, + DOMAIN, + GROUP1_NAME, + GROUP2_NAME, + GROUP3_NAME, + GROUP4_NAME, + GROUP5_NAME, + GROUP6_NAME, + HUB_EXCEPTIONS, +) + +_LOGGER = logging.getLogger(__name__) + +DATA_SCHEMA = vol.Schema( + { + vol.Required(CONF_NAME): str, + vol.Required(GROUP1_NAME): str, + vol.Optional(GROUP2_NAME): str, + vol.Optional(GROUP3_NAME): str, + vol.Optional(GROUP4_NAME): str, + vol.Optional(GROUP5_NAME): str, + vol.Optional(GROUP6_NAME): str, + } +) + + +async def validate_zone(hass: core.HomeAssistant, hub_address): + """Ensure the Leviosa Zone is up and running and get the FW version.""" + try: + _LOGGER.debug("Contacting Zone: %s", hub_address) + hub = LeviosaZoneHub( + hub_ip=hub_address, + hub_name="tempZone", + websession=async_get_clientsession(hass), + ) + await hub.getHubInfo() + _LOGGER.debug("Zone firmware v: %s", hub.fwVer) + except HUB_EXCEPTIONS as err: + raise CannotConnect from err + if hub.fwVer == "invalid": + raise CannotConnect + return hub.fwVer + + +@config_entries.HANDLERS.register(DOMAIN) +class LeviosaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Manages the interaction with user when a Leviosa Zone needs to be setup.""" + + # The schema version below will be used by Home Assistant to determine + # if a call to the migrate method is needed; this is not implemented + # as of March 2021 + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL + GROUPS = [ + GROUP1_NAME, + GROUP2_NAME, + GROUP3_NAME, + GROUP4_NAME, + GROUP5_NAME, + GROUP6_NAME, + ] + + def __init__(self): + """Initialize the Motion Blinds flow.""" + + self._host = None + self._host_uid = None + self._devices = {} + + async def async_step_user(self, user_input=None): + """Perform discovery and present an input screen for each Zone discovered.""" + + _LOGGER.debug("Looking for Leviosa Zone HUBs") + self._devices = await discover_leviosa_zones() + _LOGGER.debug("Found %d Zones advertising on the network ", len(self._devices)) + devs_2b_removed = [] + for dev_key in self._devices.keys(): + if self._host_already_configured(self._devices[dev_key]): + devs_2b_removed.append(dev_key) + for dev in devs_2b_removed: + self._devices.pop(dev) + _LOGGER.debug("There are %d Zones can be included in Hass", len(self._devices)) + Zones = list(self._devices.keys()) + if len(Zones) == 1: + self._host = self._devices[Zones[0]] + self._host_uid = Zones[0] + return await self.async_step_connect() + if len(Zones) > 1: + return await self.async_step_select() + + return self.async_abort(reason="no_new_devs") + + async def async_step_select(self, user_input=None): + """Handle multiple motion gateways found.""" + if user_input is not None: + self._host = user_input["select_ip"] + vals = list(self._devices.values()) + idx_of_ip = vals.index(self._host) + keys = list(self._devices.keys()) + self._host_uid = keys[idx_of_ip] + # self._host_uid = self._devices.keys()[ + # self._devices.values().index(self._host) + # ] + return await self.async_step_connect() + + select_schema = vol.Schema( + {vol.Required("select_ip"): vol.In(self._devices.values())} + ) + + return self.async_show_form(step_id="select", data_schema=select_schema) + + async def async_step_connect(self, user_input=None): + """Allow user to enter details for a Leviosa Zone.""" + errors = {} + if user_input is not None: + _LOGGER.debug( + "Config User step - validate and save [%s] @%s", + self._host_uid, + self._host, + ) + for i in user_input: + _LOGGER.debug("UI %s -> %s", i, user_input[i]) + + if self._host_already_configured(self._host): + return self.async_abort(reason="device_already_configured") + try: + fw_ver = await validate_zone(self.hass, self._host) + except CannotConnect: + errors["base"] = "cannot_connect" + except Exception: # pylint: disable=broad-except + _LOGGER.exception("Unexpected exception") + errors["base"] = "unknown" + if not errors: + _LOGGER.debug("Saving Integration data") + await self.async_set_unique_id(self._host_uid) + bgs = [] + bgs.append("All " + user_input[CONF_NAME]) + for group in self.GROUPS: # We'll create a list of valid groups + if user_input.get(group, "") != "": + bgs.append(user_input[group]) + else: + break + return self.async_create_entry( + title=user_input[CONF_NAME], + data={ + CONF_HOST: self._host, + DEVICE_FW_V: fw_ver, + DEVICE_MAC: self._host_uid[-12:], + BLIND_GROUPS: bgs, + }, + ) + + _LOGGER.debug("Config User step - display UI for %s", self._host) + return self.async_show_form( + step_id="connect", + data_schema=DATA_SCHEMA, + errors=errors, + description_placeholders={"ip_add": self._host}, + ) + + def _host_already_configured(self, host): + """See if we already have a hub with the host address configured.""" + _LOGGER.debug("Checking if HOST was already configured") + existing_hosts = { + entry.data.get(CONF_HOST) + for entry in self._async_current_entries() + if CONF_HOST in entry.data + } + return host in existing_hosts + + +class CannotConnect(exceptions.HomeAssistantError): + """Error to indicate we cannot connect.""" diff --git a/homeassistant/components/leviosa_shades/const.py b/homeassistant/components/leviosa_shades/const.py new file mode 100644 index 0000000000000..63db2c2a5e537 --- /dev/null +++ b/homeassistant/components/leviosa_shades/const.py @@ -0,0 +1,37 @@ +"""Constants for the Leviosa Motor Shades Zone integration.""" + +import asyncio + +from aiohttp.client_exceptions import ( + ServerConnectionError, + ServerDisconnectedError, + ServerTimeoutError, +) + +DOMAIN = "leviosa_shades" + +MANUFACTURER = "Leviosa Motor Shades LLC" +MODEL = "Zone Hub" +DEVICE_NAME = "device_name" +DEVICE_FW_V = "firmware" +DEVICE_MAC = "device_mac" + +BLIND_GROUPS = "blind_groups" +GROUP1_NAME = "grp1_name" +GROUP2_NAME = "grp2_name" +GROUP3_NAME = "grp3_name" +GROUP4_NAME = "grp4_name" +GROUP5_NAME = "grp5_name" +GROUP6_NAME = "grp6_name" + +SERVICE_NEXT_DOWN_POS = "next_down_pos" +SERVICE_NEXT_UP_POS = "next_up_pos" + +CANNOTCONNECT = "cannot_connect" + +HUB_EXCEPTIONS = ( + ServerDisconnectedError, + asyncio.TimeoutError, + ServerConnectionError, + ServerTimeoutError, +) diff --git a/homeassistant/components/leviosa_shades/cover.py b/homeassistant/components/leviosa_shades/cover.py new file mode 100644 index 0000000000000..664759a100ce7 --- /dev/null +++ b/homeassistant/components/leviosa_shades/cover.py @@ -0,0 +1,184 @@ +"""The Leviosa Shades Zone base entity.""" +import logging + +from aioleviosa import LeviosaShadeGroup as tShadeGroup, LeviosaZoneHub as tZoneHub +import voluptuous as vol + +from homeassistant.components.cover import ( + DEVICE_CLASS_SHADE, + SUPPORT_CLOSE, + SUPPORT_OPEN, + SUPPORT_STOP, + CoverEntity, +) +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.helpers import config_validation as cv, entity_platform +from homeassistant.helpers.aiohttp_client import async_get_clientsession + +from .const import ( + BLIND_GROUPS, + DOMAIN, + MANUFACTURER, + MODEL, + SERVICE_NEXT_DOWN_POS, + SERVICE_NEXT_UP_POS, +) + +_LOGGER = logging.getLogger(__name__) + +# Estimated time it takes to complete a transition +# from one state to another +TRANSITION_COMPLETE_DURATION = 30 +PARALLEL_UPDATES = 1 + +COVER_NEXT_POS_SCHEMA = vol.Schema({vol.Optional(ATTR_ENTITY_ID): cv.entity_ids}) + + +async def async_setup_entry(hass, entry, async_add_entities): + """Set up the Leviosa shade groups.""" + _LOGGER.debug( + "Setting up %s[%s]: %s", + entry.domain, + entry.title, + entry.entry_id, + ) + hub_name = entry.title + hub_mac = entry.data["device_mac"] + hub_ip = entry.data["host"] + blind_groups = entry.data[BLIND_GROUPS] + _LOGGER.debug("Groups to create: %s", blind_groups) + hub = tZoneHub( + hub_ip=hub_ip, hub_name=hub_name, websession=async_get_clientsession(hass) + ) + await hub.getHubInfo() # Check all is good + hub.groups.pop() # Remove default all group, as our entities have it already + _LOGGER.debug("Hub object created, FW: %s", hub.fwVer) + entities = [] + for blind_group in blind_groups: + _LOGGER.debug("Adding blind_group: %s", blind_group) + new_group_obj = hub.AddGroup(blind_group) + entities.append( + LeviosaBlindGroup( + hass, hub_mac + "-" + str(new_group_obj.number), new_group_obj + ) + ) + async_add_entities(entities) + + _LOGGER.debug("Setting up Leviosa shade group services") + platform = entity_platform.current_platform.get() + platform.async_register_entity_service( + SERVICE_NEXT_DOWN_POS, + COVER_NEXT_POS_SCHEMA, + "next_down_pos", + ) + platform.async_register_entity_service( + SERVICE_NEXT_UP_POS, + COVER_NEXT_POS_SCHEMA, + "next_up_pos", + ) + + +class LeviosaBlindGroup(CoverEntity): + """Represents a Leviosa shade group entity.""" + + def __init__(self, hass, blind_group_id, blind_group_obj: tShadeGroup): + """Initialize the shade group.""" + self._blind_group_id = blind_group_id + self._blind_group_obj = blind_group_obj + self._hass = hass + _LOGGER.debug( + "Creating cover.%s, UID: %s", + self._blind_group_obj.name, + self._blind_group_id, + ) + + @property + def name(self): + """Name of the device.""" + return self._blind_group_obj.name + + @property + def unique_id(self): + """Return a unique ID for this device.""" + + return self._blind_group_id + + @property + def assumed_state(self): + """Indicate that we do not go to the device to know its state.""" + + return False + + @property + def current_cover_position(self): + """Indicate that we do not go to the device to know its state.""" + return self._blind_group_obj.position + + @property + def should_poll(self): + """Indicate that the device does not respond to polling.""" + + return True + + @property + def supported_features(self): + """Bitmap indicating which features this device supports.""" + + return SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_STOP + + @property + def device_class(self): + """Indicate we're managing a Roller blind motor group.""" + + return DEVICE_CLASS_SHADE + + @property + def device_info(self): + """Return the device_info of the device.""" + + device_info = { + "identifiers": {(DOMAIN, self._blind_group_obj.Hub.hub_ip)}, + "name": self._blind_group_obj.Hub.name, + "manufacturer": MANUFACTURER, + "model": MODEL, + "via_device": (DOMAIN, self._blind_group_obj.Hub.hub_ip), + } + + return device_info + + @property + def is_opening(self): + """Is the blind group opening?.""" + + return False + + @property + def is_closing(self): + """Is the blind closing?.""" + + return False + + @property + def is_closed(self): + """Is the blind group currently closed?.""" + return self._blind_group_obj.position == 0 + + async def async_close_cover(self, **kwargs): + """Close the cover.""" + await self._blind_group_obj.close() + + async def async_open_cover(self, **kwargs): + """Open the cover.""" + await self._blind_group_obj.open() + + async def async_stop_cover(self, **kwargs): + """Stop the cover.""" + await self._blind_group_obj.stop() + + async def next_down_pos(self): + """Move to the next position down.""" + await self._blind_group_obj.down() + + async def next_up_pos(self): + """Move to the next position down.""" + await self._blind_group_obj.up() diff --git a/homeassistant/components/leviosa_shades/manifest.json b/homeassistant/components/leviosa_shades/manifest.json new file mode 100644 index 0000000000000..b21a87640cf9a --- /dev/null +++ b/homeassistant/components/leviosa_shades/manifest.json @@ -0,0 +1,12 @@ +{ + "domain": "leviosa_shades", + "name": "Leviosa Motor Shades", + "documentation": "https://www.home-assistant.io/integrations/leviosa_shades", + "requirements": [ + "async-upnp-client==0.14.13", + "aioleviosa==0.1" + ], + "codeowners": ["@altersis"], + "dependencies": ["cover"], + "config_flow": true +} diff --git a/homeassistant/components/leviosa_shades/services.yaml b/homeassistant/components/leviosa_shades/services.yaml new file mode 100644 index 0000000000000..32970f4166150 --- /dev/null +++ b/homeassistant/components/leviosa_shades/services.yaml @@ -0,0 +1,17 @@ +next_down_pos: + description: Moves a blind group down to the next defined intermediate position + fields: + entity_id: + description: Name(s) of entities to move down to the next position. + example: "cover.master" + default: "cover.master" + required: true + +next_up_pos: + description: Moves a blind group down to the next defined intermediate position + fields: + entity_id: + description: Name(s) of entities to move up to the next position. + example: "cover.master" + default: "cover.master" + required: true diff --git a/homeassistant/components/leviosa_shades/strings.json b/homeassistant/components/leviosa_shades/strings.json new file mode 100644 index 0000000000000..c9ca9cdb09509 --- /dev/null +++ b/homeassistant/components/leviosa_shades/strings.json @@ -0,0 +1,26 @@ +{ + "title": "Leviosa Motor Shades Zone", + "config": { + "step": { + "user": { + "title": "Connect to Leviosa Motor Shades Zone HUB", + "data": { + "name": "[%key:common::config_flow::data::name%]", + "host": "[%key:common::config_flow::data::ip%]" + } + }, + "confirm": { + "description": "[%key:common::config_flow::description::confirm_setup%]" + } + }, + "error": { + "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]", + "unknown": "[%key:common::config_flow::error::unknown%]", + "no_new_devs": "No unconfigured Leviosa Zone HUBs found" + }, + "abort": { + "no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]", + "no_new_devs": "No unconfigured Leviosa Zone HUBs found" + } + } +} diff --git a/homeassistant/components/leviosa_shades/translations/en.json b/homeassistant/components/leviosa_shades/translations/en.json new file mode 100644 index 0000000000000..247e63670083c --- /dev/null +++ b/homeassistant/components/leviosa_shades/translations/en.json @@ -0,0 +1,42 @@ +{ + "config": { + "abort": { + "no_devices_found": "No devices found on the network", + "single_instance_allowed": "Already configured. Only a single configuration possible.", + "device_already_configured": "This Leviosa Zone is already configured", + "no_new_devs": "No unconfigured Leviosa Zone HUBs found" + }, + "error": { + "cannot_connect": "The Leviosa Zone HUB does not appear to be online", + "unknown": "There was an unexpected error connecting to the Leviosa Zone HUB", + "no_new_devs": "No unconfigured Leviosa Zone HUBs found" + }, + "step": { + "select": { + "data": { + "select_ip": "IP Address" + }, + "description": "Run the setup again if you want to connect another Leviosa Zone", + "title": "Which Zone Hub would you like to configure?" + }, + "connect": { + "title": "Adding Leviosa Motor Shades Zone", + "description": "Details for Zone at: {ip_add}", + "data": { + "name": "Zone HUB Name", + "host": "Zone IP Address", + "grp1_name": "Group 1 name", + "grp2_name": "Group 2 name", + "grp3_name": "Group 3 name", + "grp4_name": "Group 4 name", + "grp5_name": "Group 5 name", + "grp6_name": "Group 6 name" + } + }, + "confirm": { + "description": "Ready to setup this device?" + } + } + }, + "title": "Leviosa Motor Shades Zone" +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index a3bcef9047f26..0cf2a0008e904 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -121,6 +121,7 @@ "kodi", "konnected", "kulersky", + "leviosa_shades", "life360", "lifx", "litejet", diff --git a/requirements_all.txt b/requirements_all.txt index d1e27d28076eb..263d553cdb697 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -187,6 +187,9 @@ aiokafka==0.6.0 # homeassistant.components.kef aiokef==0.2.16 +# homeassistant.components.leviosa_shades +aioleviosa==0.1 + # homeassistant.components.lifx aiolifx==0.6.9 @@ -284,6 +287,7 @@ asmog==0.0.6 asterisk_mbox==0.5.0 # homeassistant.components.dlna_dmr +# homeassistant.components.leviosa_shades # homeassistant.components.ssdp # homeassistant.components.upnp async-upnp-client==0.14.13 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ed005d34d03b3..dbaedb103d002 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -115,6 +115,9 @@ aiohue==2.1.0 # homeassistant.components.apache_kafka aiokafka==0.6.0 +# homeassistant.components.leviosa_shades +aioleviosa==0.1 + # homeassistant.components.lutron_caseta aiolip==1.1.4 @@ -173,6 +176,7 @@ aprslib==0.6.46 arcam-fmj==0.5.3 # homeassistant.components.dlna_dmr +# homeassistant.components.leviosa_shades # homeassistant.components.ssdp # homeassistant.components.upnp async-upnp-client==0.14.13 diff --git a/tests/components/leviosa_shades/__init__.py b/tests/components/leviosa_shades/__init__.py new file mode 100644 index 0000000000000..b4ff037ff84d6 --- /dev/null +++ b/tests/components/leviosa_shades/__init__.py @@ -0,0 +1 @@ +"""Tests for the Leviosa Motor Shades Zone integration.""" diff --git a/tests/components/leviosa_shades/test_config_flow.py b/tests/components/leviosa_shades/test_config_flow.py new file mode 100644 index 0000000000000..a5d01b5905ff1 --- /dev/null +++ b/tests/components/leviosa_shades/test_config_flow.py @@ -0,0 +1,94 @@ +"""Test the Leviosa Motor Shades Zone config flow.""" +from unittest.mock import patch + +from homeassistant import config_entries, setup +from homeassistant.components.leviosa_shades.config_flow import ( + CannotConnect, + InvalidAuth, +) +from homeassistant.components.leviosa_shades.const import DOMAIN + + +async def test_form(hass): + """Test we get the form.""" + await setup.async_setup_component(hass, "persistent_notification", {}) + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["errors"] == {} + + with patch( + "homeassistant.components.leviosa_shades.config_flow.PlaceholderHub.authenticate", + return_value=True, + ), patch( + "homeassistant.components.leviosa_shades.async_setup", return_value=True + ) as mock_setup, patch( + "homeassistant.components.leviosa_shades.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) + await hass.async_block_till_done() + + assert result2["type"] == "create_entry" + assert result2["title"] == "Name of the device" + assert result2["data"] == { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + } + assert len(mock_setup.mock_calls) == 1 + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_form_invalid_auth(hass): + """Test we handle invalid auth.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.leviosa_shades.config_flow.PlaceholderHub.authenticate", + side_effect=InvalidAuth, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "invalid_auth"} + + +async def test_form_cannot_connect(hass): + """Test we handle cannot connect error.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + with patch( + "homeassistant.components.leviosa_shades.config_flow.PlaceholderHub.authenticate", + side_effect=CannotConnect, + ): + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + { + "host": "1.1.1.1", + "username": "test-username", + "password": "test-password", + }, + ) + + assert result2["type"] == "form" + assert result2["errors"] == {"base": "cannot_connect"} From 3935a197104629bae3fe7fbdb0d0c828e5996a5e Mon Sep 17 00:00:00 2001 From: Gerardo Castillo <32140109+altersis@users.noreply.github.com> Date: Wed, 17 Mar 2021 20:28:24 +0000 Subject: [PATCH 2/8] Removed last pieces of commented code --- homeassistant/components/leviosa_shades/__init__.py | 4 ---- homeassistant/components/leviosa_shades/config_flow.py | 3 --- 2 files changed, 7 deletions(-) diff --git a/homeassistant/components/leviosa_shades/__init__.py b/homeassistant/components/leviosa_shades/__init__.py index 30beb9613434b..138828ace5820 100644 --- a/homeassistant/components/leviosa_shades/__init__.py +++ b/homeassistant/components/leviosa_shades/__init__.py @@ -36,9 +36,5 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): ] ) ) - # if unload_ok: - # _LOGGER.debug("data: %s", entry.data) - # hass.data[DOMAIN].pop(entry.entry_id) - # # hass.data["cover"].pop(entry.entry_id) return unload_ok diff --git a/homeassistant/components/leviosa_shades/config_flow.py b/homeassistant/components/leviosa_shades/config_flow.py index dd79e0eed71f7..feb6a20734959 100644 --- a/homeassistant/components/leviosa_shades/config_flow.py +++ b/homeassistant/components/leviosa_shades/config_flow.py @@ -111,9 +111,6 @@ async def async_step_select(self, user_input=None): idx_of_ip = vals.index(self._host) keys = list(self._devices.keys()) self._host_uid = keys[idx_of_ip] - # self._host_uid = self._devices.keys()[ - # self._devices.values().index(self._host) - # ] return await self.async_step_connect() select_schema = vol.Schema( From b61c41f457680fe8281cc4a87772e3efc6f8c096 Mon Sep 17 00:00:00 2001 From: Gerardo Castillo <32140109+altersis@users.noreply.github.com> Date: Mon, 22 Mar 2021 13:15:05 +0000 Subject: [PATCH 3/8] fixed case of found zones (z instead of Z) --- homeassistant/components/leviosa_shades/config_flow.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/homeassistant/components/leviosa_shades/config_flow.py b/homeassistant/components/leviosa_shades/config_flow.py index feb6a20734959..9e10a48bc1aff 100644 --- a/homeassistant/components/leviosa_shades/config_flow.py +++ b/homeassistant/components/leviosa_shades/config_flow.py @@ -93,12 +93,12 @@ async def async_step_user(self, user_input=None): for dev in devs_2b_removed: self._devices.pop(dev) _LOGGER.debug("There are %d Zones can be included in Hass", len(self._devices)) - Zones = list(self._devices.keys()) - if len(Zones) == 1: - self._host = self._devices[Zones[0]] - self._host_uid = Zones[0] + zones = list(self._devices.keys()) + if len(zones) == 1: + self._host = self._devices[zones[0]] + self._host_uid = zones[0] return await self.async_step_connect() - if len(Zones) > 1: + if len(zones) > 1: return await self.async_step_select() return self.async_abort(reason="no_new_devs") From 6f73f65376d0b6260b72683c68f7dac1904399b4 Mon Sep 17 00:00:00 2001 From: Gerardo Castillo <32140109+altersis@users.noreply.github.com> Date: Tue, 23 Mar 2021 12:24:39 +0000 Subject: [PATCH 4/8] updated to support v0.2.0 of aioleviosa --- .coveragerc | 1 + .../components/leviosa_shades/cover.py | 1 - .../components/leviosa_shades/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../leviosa_shades/test_config_flow.py | 46 ++----------------- 6 files changed, 8 insertions(+), 46 deletions(-) diff --git a/.coveragerc b/.coveragerc index d6bdfb9b09181..58218da28cb9b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -517,6 +517,7 @@ omit = homeassistant/components/lcn/sensor.py homeassistant/components/lcn/services.py homeassistant/components/lcn/switch.py + homeassistant/components/leviosa_shades/cover.py homeassistant/components/lg_netcast/media_player.py homeassistant/components/lg_soundbar/media_player.py homeassistant/components/life360/* diff --git a/homeassistant/components/leviosa_shades/cover.py b/homeassistant/components/leviosa_shades/cover.py index 664759a100ce7..dee9252ae7a0a 100644 --- a/homeassistant/components/leviosa_shades/cover.py +++ b/homeassistant/components/leviosa_shades/cover.py @@ -51,7 +51,6 @@ async def async_setup_entry(hass, entry, async_add_entities): hub_ip=hub_ip, hub_name=hub_name, websession=async_get_clientsession(hass) ) await hub.getHubInfo() # Check all is good - hub.groups.pop() # Remove default all group, as our entities have it already _LOGGER.debug("Hub object created, FW: %s", hub.fwVer) entities = [] for blind_group in blind_groups: diff --git a/homeassistant/components/leviosa_shades/manifest.json b/homeassistant/components/leviosa_shades/manifest.json index b21a87640cf9a..360a36f9cbc92 100644 --- a/homeassistant/components/leviosa_shades/manifest.json +++ b/homeassistant/components/leviosa_shades/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/leviosa_shades", "requirements": [ "async-upnp-client==0.14.13", - "aioleviosa==0.1" + "aioleviosa==0.2.0" ], "codeowners": ["@altersis"], "dependencies": ["cover"], diff --git a/requirements_all.txt b/requirements_all.txt index 263d553cdb697..80667fca95617 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -188,7 +188,7 @@ aiokafka==0.6.0 aiokef==0.2.16 # homeassistant.components.leviosa_shades -aioleviosa==0.1 +aioleviosa==0.2.0 # homeassistant.components.lifx aiolifx==0.6.9 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index dbaedb103d002..d5fd70c6d81f0 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -116,7 +116,7 @@ aiohue==2.1.0 aiokafka==0.6.0 # homeassistant.components.leviosa_shades -aioleviosa==0.1 +aioleviosa==0.2.0 # homeassistant.components.lutron_caseta aiolip==1.1.4 diff --git a/tests/components/leviosa_shades/test_config_flow.py b/tests/components/leviosa_shades/test_config_flow.py index a5d01b5905ff1..96b8e0de46853 100644 --- a/tests/components/leviosa_shades/test_config_flow.py +++ b/tests/components/leviosa_shades/test_config_flow.py @@ -2,10 +2,7 @@ from unittest.mock import patch from homeassistant import config_entries, setup -from homeassistant.components.leviosa_shades.config_flow import ( - CannotConnect, - InvalidAuth, -) +from homeassistant.components.leviosa_shades.config_flow import CannotConnect from homeassistant.components.leviosa_shades.const import DOMAIN @@ -29,48 +26,17 @@ async def test_form(hass): ) as mock_setup_entry: result2 = await hass.config_entries.flow.async_configure( result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, + {"host": "1.1.1.1"}, ) await hass.async_block_till_done() assert result2["type"] == "create_entry" assert result2["title"] == "Name of the device" - assert result2["data"] == { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - } + assert result2["data"] == {"host": "1.1.1.1"} assert len(mock_setup.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1 -async def test_form_invalid_auth(hass): - """Test we handle invalid auth.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - with patch( - "homeassistant.components.leviosa_shades.config_flow.PlaceholderHub.authenticate", - side_effect=InvalidAuth, - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, - ) - - assert result2["type"] == "form" - assert result2["errors"] == {"base": "invalid_auth"} - - async def test_form_cannot_connect(hass): """Test we handle cannot connect error.""" result = await hass.config_entries.flow.async_init( @@ -83,11 +49,7 @@ async def test_form_cannot_connect(hass): ): result2 = await hass.config_entries.flow.async_configure( result["flow_id"], - { - "host": "1.1.1.1", - "username": "test-username", - "password": "test-password", - }, + {"host": "1.1.1.1"}, ) assert result2["type"] == "form" From 24f244d0dbb8802794f357901d4233d1c8d6d390 Mon Sep 17 00:00:00 2001 From: Gerardo Castillo <32140109+altersis@users.noreply.github.com> Date: Tue, 23 Mar 2021 21:31:51 +0000 Subject: [PATCH 5/8] Fixed test cases and minor debug statement --- .../components/leviosa_shades/config_flow.py | 6 +- .../leviosa_shades/test_config_flow.py | 119 ++++++++++++------ 2 files changed, 82 insertions(+), 43 deletions(-) diff --git a/homeassistant/components/leviosa_shades/config_flow.py b/homeassistant/components/leviosa_shades/config_flow.py index 9e10a48bc1aff..4e9e7fe4382bb 100644 --- a/homeassistant/components/leviosa_shades/config_flow.py +++ b/homeassistant/components/leviosa_shades/config_flow.py @@ -116,7 +116,7 @@ async def async_step_select(self, user_input=None): select_schema = vol.Schema( {vol.Required("select_ip"): vol.In(self._devices.values())} ) - + _LOGGER.debug("Select Zone to include in Hass, %s choices", len(self._devices)) return self.async_show_form(step_id="select", data_schema=select_schema) async def async_step_connect(self, user_input=None): @@ -124,7 +124,7 @@ async def async_step_connect(self, user_input=None): errors = {} if user_input is not None: _LOGGER.debug( - "Config User step - validate and save [%s] @%s", + "Connect step - validate and save [%s] @%s", self._host_uid, self._host, ) @@ -160,7 +160,7 @@ async def async_step_connect(self, user_input=None): }, ) - _LOGGER.debug("Config User step - display UI for %s", self._host) + _LOGGER.debug("Connect step - display UI for %s", self._host) return self.async_show_form( step_id="connect", data_schema=DATA_SCHEMA, diff --git a/tests/components/leviosa_shades/test_config_flow.py b/tests/components/leviosa_shades/test_config_flow.py index 96b8e0de46853..82d34dacdd79b 100644 --- a/tests/components/leviosa_shades/test_config_flow.py +++ b/tests/components/leviosa_shades/test_config_flow.py @@ -1,56 +1,95 @@ """Test the Leviosa Motor Shades Zone config flow.""" from unittest.mock import patch -from homeassistant import config_entries, setup -from homeassistant.components.leviosa_shades.config_flow import CannotConnect -from homeassistant.components.leviosa_shades.const import DOMAIN +import pytest +from homeassistant import config_entries +from homeassistant.components.leviosa_shades.const import ( + BLIND_GROUPS, + DEVICE_FW_V, + DEVICE_MAC, + DOMAIN, + GROUP1_NAME, + GROUP2_NAME, + GROUP3_NAME, + GROUP4_NAME, +) +from homeassistant.const import CONF_HOST, CONF_NAME -async def test_form(hass): - """Test we get the form.""" - await setup.async_setup_component(hass, "persistent_notification", {}) - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] == "form" - assert result["errors"] == {} +TEST_HOST1 = "1.2.3.4" +TEST_HOST2 = "5.6.7.8" +TEST_MAC1 = "40f5205b658c" +TEST_MAC2 = "40f5205b6687" + +TEST_ZONE_FW = "8.3" + +TEST_DISCOVERY_1 = {"uid:6bf25702-1d6a-4c7b-b949-40f5205b658c": TEST_HOST1} +TEST_DISCOVERY_2 = { + "uid:6bf25702-1d6a-4c7b-b949-40f5205b658c": TEST_HOST1, + "uid:6bf25702-1d6a-4c7b-b949-40f5205b6687": TEST_HOST2, +} +TEST_USER_INPUT_1 = { + CONF_NAME: "Zone 1", + GROUP1_NAME: "Z1 Group 1", + GROUP2_NAME: "Z1 Group 2", + GROUP3_NAME: "Z1 Group 3", + GROUP4_NAME: "Z1 Group 4", +} +TEST_USER_INPUT_2 = { + CONF_NAME: "Zone 2", + GROUP1_NAME: "Z2 Group 1", + GROUP2_NAME: "Z2 Group 2", + GROUP3_NAME: "Z2 Group 3", + GROUP4_NAME: "Z2 Group 4", +} + + +@pytest.fixture(name="leviosa_shades_connect", autouse=True) +def leviosa_shades_connect_fixture(): + """Mock motion blinds connection and entry setup.""" with patch( - "homeassistant.components.leviosa_shades.config_flow.PlaceholderHub.authenticate", - return_value=True, + "homeassistant.components.leviosa_shades.config_flow.discover_leviosa_zones", + return_value=TEST_DISCOVERY_1, ), patch( - "homeassistant.components.leviosa_shades.async_setup", return_value=True - ) as mock_setup, patch( - "homeassistant.components.leviosa_shades.async_setup_entry", - return_value=True, - ) as mock_setup_entry: - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"host": "1.1.1.1"}, - ) - await hass.async_block_till_done() + "homeassistant.components.leviosa_shades.async_setup_entry", return_value=True + ): + yield - assert result2["type"] == "create_entry" - assert result2["title"] == "Name of the device" - assert result2["data"] == {"host": "1.1.1.1"} - assert len(mock_setup.mock_calls) == 1 - assert len(mock_setup_entry.mock_calls) == 1 +async def test_config_flow_one_zone_success(hass): + """Successful flow initiated by the user, one Zone discovered.""" + with patch( + "homeassistant.components.leviosa_shades.config_flow.discover_leviosa_zones", + return_value=TEST_DISCOVERY_1, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) -async def test_form_cannot_connect(hass): - """Test we handle cannot connect error.""" - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) + assert result["type"] == "form" + assert result["step_id"] == "connect" + assert result["errors"] == {} with patch( - "homeassistant.components.leviosa_shades.config_flow.PlaceholderHub.authenticate", - side_effect=CannotConnect, + "homeassistant.components.leviosa_shades.config_flow.validate_zone", + return_value=TEST_ZONE_FW, ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"host": "1.1.1.1"}, + result = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_USER_INPUT_1 ) - assert result2["type"] == "form" - assert result2["errors"] == {"base": "cannot_connect"} + assert result["type"] == "create_entry" + assert result["title"] == TEST_USER_INPUT_1[CONF_NAME] + assert result["data"] == { + CONF_HOST: TEST_HOST1, + DEVICE_FW_V: TEST_ZONE_FW, + DEVICE_MAC: TEST_MAC1, + BLIND_GROUPS: [ + "All Zone 1", + "Z1 Group 1", + "Z1 Group 2", + "Z1 Group 3", + "Z1 Group 4", + ], + } From b394187a98313e8ab2a28917106e62f713b033ab Mon Sep 17 00:00:00 2001 From: Gerardo Castillo <32140109+altersis@users.noreply.github.com> Date: Wed, 24 Mar 2021 18:00:20 +0000 Subject: [PATCH 6/8] More test cases coverage, better config_flow code --- .../components/leviosa_shades/config_flow.py | 6 +- .../leviosa_shades/test_config_flow.py | 100 ++++++++++++++++-- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/homeassistant/components/leviosa_shades/config_flow.py b/homeassistant/components/leviosa_shades/config_flow.py index 4e9e7fe4382bb..878d72809efd2 100644 --- a/homeassistant/components/leviosa_shades/config_flow.py +++ b/homeassistant/components/leviosa_shades/config_flow.py @@ -114,7 +114,7 @@ async def async_step_select(self, user_input=None): return await self.async_step_connect() select_schema = vol.Schema( - {vol.Required("select_ip"): vol.In(self._devices.values())} + {vol.Required("select_ip"): vol.In(list(self._devices.values()))} ) _LOGGER.debug("Select Zone to include in Hass, %s choices", len(self._devices)) return self.async_show_form(step_id="select", data_schema=select_schema) @@ -131,8 +131,8 @@ async def async_step_connect(self, user_input=None): for i in user_input: _LOGGER.debug("UI %s -> %s", i, user_input[i]) - if self._host_already_configured(self._host): - return self.async_abort(reason="device_already_configured") + # if self._host_already_configured(self._host): + # return self.async_abort(reason="device_already_configured") try: fw_ver = await validate_zone(self.hass, self._host) except CannotConnect: diff --git a/tests/components/leviosa_shades/test_config_flow.py b/tests/components/leviosa_shades/test_config_flow.py index 82d34dacdd79b..8c4ab02d505fb 100644 --- a/tests/components/leviosa_shades/test_config_flow.py +++ b/tests/components/leviosa_shades/test_config_flow.py @@ -1,8 +1,6 @@ """Test the Leviosa Motor Shades Zone config flow.""" from unittest.mock import patch -import pytest - from homeassistant import config_entries from homeassistant.components.leviosa_shades.const import ( BLIND_GROUPS, @@ -22,7 +20,9 @@ TEST_MAC2 = "40f5205b6687" TEST_ZONE_FW = "8.3" +TEST_ZONE_FW_ALT = "0.0.0" +TEST_DISCOVERY_0 = {} TEST_DISCOVERY_1 = {"uid:6bf25702-1d6a-4c7b-b949-40f5205b658c": TEST_HOST1} TEST_DISCOVERY_2 = { "uid:6bf25702-1d6a-4c7b-b949-40f5205b658c": TEST_HOST1, @@ -45,20 +45,46 @@ } -@pytest.fixture(name="leviosa_shades_connect", autouse=True) -def leviosa_shades_connect_fixture(): - """Mock motion blinds connection and entry setup.""" +async def test_config_flow_one_zone_success(hass): + """Successful flow initiated by the user, one Zone discovered.""" with patch( "homeassistant.components.leviosa_shades.config_flow.discover_leviosa_zones", return_value=TEST_DISCOVERY_1, - ), patch( - "homeassistant.components.leviosa_shades.async_setup_entry", return_value=True ): - yield + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["step_id"] == "connect" + assert result["errors"] == {} -async def test_config_flow_one_zone_success(hass): - """Successful flow initiated by the user, one Zone discovered.""" + with patch( + "homeassistant.components.leviosa_shades.config_flow.LeviosaZoneHub.getHubInfo", + return_value=TEST_ZONE_FW_ALT, + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_USER_INPUT_1 + ) + + assert result["type"] == "create_entry" + assert result["title"] == TEST_USER_INPUT_1[CONF_NAME] + assert result["data"] == { + CONF_HOST: TEST_HOST1, + DEVICE_FW_V: TEST_ZONE_FW_ALT, + DEVICE_MAC: TEST_MAC1, + BLIND_GROUPS: [ + "All Zone 1", + "Z1 Group 1", + "Z1 Group 2", + "Z1 Group 3", + "Z1 Group 4", + ], + } + + +async def test_config_flow_one_zone_failure(hass): + """Flow started, one Zone discovered, not responding afterwards.""" with patch( "homeassistant.components.leviosa_shades.config_flow.discover_leviosa_zones", return_value=TEST_DISCOVERY_1, @@ -71,6 +97,46 @@ async def test_config_flow_one_zone_success(hass): assert result["step_id"] == "connect" assert result["errors"] == {} + with patch( + "homeassistant.components.leviosa_shades.config_flow.LeviosaZoneHub.fwVer", + return_value="invalid", + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], TEST_USER_INPUT_1 + ) + + assert result["type"] == "form" + assert result["step_id"] == "connect" + assert result["errors"] != {} + + +async def test_config_flow_two_zone_success(hass): + """Successful flow initiated by the user, two Zones discovered, one selected.""" + with patch( + "homeassistant.components.leviosa_shades.config_flow.discover_leviosa_zones", + return_value=TEST_DISCOVERY_2, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == "form" + assert result["step_id"] == "select" + assert result["data_schema"].schema["select_ip"].container == [ + TEST_HOST1, + TEST_HOST2, + ] + assert result["errors"] is None + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {"select_ip": TEST_HOST1}, + ) + + assert result["type"] == "form" + assert result["step_id"] == "connect" + assert result["errors"] == {} + with patch( "homeassistant.components.leviosa_shades.config_flow.validate_zone", return_value=TEST_ZONE_FW, @@ -93,3 +159,17 @@ async def test_config_flow_one_zone_success(hass): "Z1 Group 4", ], } + + +async def test_config_flow_no_zone_abort(hass): + """Flow initiated by user, no Zones discovered.""" + with patch( + "homeassistant.components.leviosa_shades.config_flow.discover_leviosa_zones", + return_value=TEST_DISCOVERY_0, + ): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + + assert result["type"] == "abort" + assert result["reason"] == "no_new_devs" From 2a4a530968814418e7088d26eee3a0e1fa2f9243 Mon Sep 17 00:00:00 2001 From: Gerardo Castillo <32140109+altersis@users.noreply.github.com> Date: Wed, 24 Mar 2021 18:58:48 +0000 Subject: [PATCH 7/8] Removed test case interacting with device --- .../leviosa_shades/test_config_flow.py | 33 ++----------------- 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/tests/components/leviosa_shades/test_config_flow.py b/tests/components/leviosa_shades/test_config_flow.py index 8c4ab02d505fb..9202435cefd71 100644 --- a/tests/components/leviosa_shades/test_config_flow.py +++ b/tests/components/leviosa_shades/test_config_flow.py @@ -60,8 +60,8 @@ async def test_config_flow_one_zone_success(hass): assert result["errors"] == {} with patch( - "homeassistant.components.leviosa_shades.config_flow.LeviosaZoneHub.getHubInfo", - return_value=TEST_ZONE_FW_ALT, + "homeassistant.components.leviosa_shades.config_flow.validate_zone", + return_value=TEST_ZONE_FW, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], TEST_USER_INPUT_1 @@ -71,7 +71,7 @@ async def test_config_flow_one_zone_success(hass): assert result["title"] == TEST_USER_INPUT_1[CONF_NAME] assert result["data"] == { CONF_HOST: TEST_HOST1, - DEVICE_FW_V: TEST_ZONE_FW_ALT, + DEVICE_FW_V: TEST_ZONE_FW, DEVICE_MAC: TEST_MAC1, BLIND_GROUPS: [ "All Zone 1", @@ -83,33 +83,6 @@ async def test_config_flow_one_zone_success(hass): } -async def test_config_flow_one_zone_failure(hass): - """Flow started, one Zone discovered, not responding afterwards.""" - with patch( - "homeassistant.components.leviosa_shades.config_flow.discover_leviosa_zones", - return_value=TEST_DISCOVERY_1, - ): - result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - - assert result["type"] == "form" - assert result["step_id"] == "connect" - assert result["errors"] == {} - - with patch( - "homeassistant.components.leviosa_shades.config_flow.LeviosaZoneHub.fwVer", - return_value="invalid", - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], TEST_USER_INPUT_1 - ) - - assert result["type"] == "form" - assert result["step_id"] == "connect" - assert result["errors"] != {} - - async def test_config_flow_two_zone_success(hass): """Successful flow initiated by the user, two Zones discovered, one selected.""" with patch( From 5ac73d31229dc62cd786d4da70c22186f8ae6e44 Mon Sep 17 00:00:00 2001 From: Gerardo Castillo <32140109+altersis@users.noreply.github.com> Date: Wed, 24 Mar 2021 19:48:15 +0000 Subject: [PATCH 8/8] Added test fixture back --- .../components/leviosa_shades/test_config_flow.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/components/leviosa_shades/test_config_flow.py b/tests/components/leviosa_shades/test_config_flow.py index 9202435cefd71..e89f8d3239aa1 100644 --- a/tests/components/leviosa_shades/test_config_flow.py +++ b/tests/components/leviosa_shades/test_config_flow.py @@ -1,6 +1,8 @@ """Test the Leviosa Motor Shades Zone config flow.""" from unittest.mock import patch +import pytest + from homeassistant import config_entries from homeassistant.components.leviosa_shades.const import ( BLIND_GROUPS, @@ -45,6 +47,18 @@ } +@pytest.fixture(name="leviosa_shades_connect", autouse=True) +def leviosa_shades_connect_fixture(): + """Mock motion blinds connection and entry setup.""" + with patch( + "homeassistant.components.leviosa_shades.config_flow.discover_leviosa_zones", + return_value=TEST_DISCOVERY_1, + ), patch( + "homeassistant.components.leviosa_shades.async_setup_entry", return_value=True + ): + yield + + async def test_config_flow_one_zone_success(hass): """Successful flow initiated by the user, one Zone discovered.""" with patch(