Add Xiaomi Miio vacuum config flow (#46669)

pull/46903/head
starkillerOG 2021-02-22 13:01:02 +01:00 committed by GitHub
parent 23c2bd4e69
commit 338c07a56b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 278 additions and 189 deletions

View File

@ -17,6 +17,7 @@ from .const import (
DOMAIN,
KEY_COORDINATOR,
MODELS_SWITCH,
MODELS_VACUUM,
)
from .gateway import ConnectXiaomiGateway
@ -24,6 +25,7 @@ _LOGGER = logging.getLogger(__name__)
GATEWAY_PLATFORMS = ["alarm_control_panel", "sensor", "light"]
SWITCH_PLATFORMS = ["switch"]
VACUUM_PLATFORMS = ["vacuum"]
async def async_setup(hass: core.HomeAssistant, config: dict):
@ -117,9 +119,14 @@ async def async_setup_device_entry(
model = entry.data[CONF_MODEL]
# Identify platforms to setup
platforms = []
if model in MODELS_SWITCH:
platforms = SWITCH_PLATFORMS
else:
for vacuum_model in MODELS_VACUUM:
if model.startswith(vacuum_model):
platforms = VACUUM_PLATFORMS
if not platforms:
return False
for component in platforms:

View File

@ -15,8 +15,9 @@ from .const import (
CONF_MAC,
CONF_MODEL,
DOMAIN,
MODELS_ALL,
MODELS_ALL_DEVICES,
MODELS_GATEWAY,
MODELS_SWITCH,
)
from .device import ConnectXiaomiDevice
@ -29,6 +30,7 @@ DEVICE_SETTINGS = {
vol.Required(CONF_TOKEN): vol.All(str, vol.Length(min=32, max=32)),
}
DEVICE_CONFIG = vol.Schema({vol.Required(CONF_HOST): str}).extend(DEVICE_SETTINGS)
DEVICE_MODEL_CONFIG = {vol.Optional(CONF_MODEL): vol.In(MODELS_ALL)}
class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
@ -40,6 +42,7 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
def __init__(self):
"""Initialize."""
self.host = None
self.mac = None
async def async_step_import(self, conf: dict):
"""Import a configuration from config.yaml."""
@ -53,15 +56,15 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle zeroconf discovery."""
name = discovery_info.get("name")
self.host = discovery_info.get("host")
mac_address = discovery_info.get("properties", {}).get("mac")
self.mac = discovery_info.get("properties", {}).get("mac")
if not name or not self.host or not mac_address:
if not name or not self.host or not self.mac:
return self.async_abort(reason="not_xiaomi_miio")
# Check which device is discovered.
for gateway_model in MODELS_GATEWAY:
if name.startswith(gateway_model.replace(".", "-")):
unique_id = format_mac(mac_address)
unique_id = format_mac(self.mac)
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured({CONF_HOST: self.host})
@ -70,9 +73,9 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
)
return await self.async_step_device()
for switch_model in MODELS_SWITCH:
if name.startswith(switch_model.replace(".", "-")):
unique_id = format_mac(mac_address)
for device_model in MODELS_ALL_DEVICES:
if name.startswith(device_model.replace(".", "-")):
unique_id = format_mac(self.mac)
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured({CONF_HOST: self.host})
@ -95,6 +98,7 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
errors = {}
if user_input is not None:
token = user_input[CONF_TOKEN]
model = user_input.get(CONF_MODEL)
if user_input.get(CONF_HOST):
self.host = user_input[CONF_HOST]
@ -103,12 +107,17 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
await connect_device_class.async_connect_device(self.host, token)
device_info = connect_device_class.device_info
if device_info is not None:
if model is None and device_info is not None:
model = device_info.model
if model is not None:
if self.mac is None and device_info is not None:
self.mac = format_mac(device_info.mac_address)
# Setup Gateways
for gateway_model in MODELS_GATEWAY:
if device_info.model.startswith(gateway_model):
mac = format_mac(device_info.mac_address)
unique_id = mac
if model.startswith(gateway_model):
unique_id = self.mac
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
@ -117,29 +126,29 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
CONF_FLOW_TYPE: CONF_GATEWAY,
CONF_HOST: self.host,
CONF_TOKEN: token,
CONF_MODEL: device_info.model,
CONF_MAC: mac,
CONF_MODEL: model,
CONF_MAC: self.mac,
},
)
# Setup all other Miio Devices
name = user_input.get(CONF_NAME, DEFAULT_DEVICE_NAME)
if device_info.model in MODELS_SWITCH:
mac = format_mac(device_info.mac_address)
unique_id = mac
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=name,
data={
CONF_FLOW_TYPE: CONF_DEVICE,
CONF_HOST: self.host,
CONF_TOKEN: token,
CONF_MODEL: device_info.model,
CONF_MAC: mac,
},
)
for device_model in MODELS_ALL_DEVICES:
if model.startswith(device_model):
unique_id = self.mac
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=name,
data={
CONF_FLOW_TYPE: CONF_DEVICE,
CONF_HOST: self.host,
CONF_TOKEN: token,
CONF_MODEL: model,
CONF_MAC: self.mac,
},
)
errors["base"] = "unknown_device"
else:
errors["base"] = "cannot_connect"
@ -149,4 +158,7 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
else:
schema = DEVICE_CONFIG
if errors:
schema = schema.extend(DEVICE_MODEL_CONFIG)
return self.async_show_form(step_id="device", data_schema=schema, errors=errors)

View File

@ -23,6 +23,10 @@ MODELS_SWITCH = [
"chuangmi.plug.hmi206",
"lumi.acpartner.v3",
]
MODELS_VACUUM = ["roborock.vacuum"]
MODELS_ALL_DEVICES = MODELS_SWITCH + MODELS_VACUUM
MODELS_ALL = MODELS_ALL_DEVICES + MODELS_GATEWAY
# Fan Services
SERVICE_SET_BUZZER_ON = "fan_set_buzzer_on"

View File

@ -78,10 +78,14 @@ class XiaomiMiioEntity(Entity):
@property
def device_info(self):
"""Return the device info."""
return {
"connections": {(dr.CONNECTION_NETWORK_MAC, self._mac)},
device_info = {
"identifiers": {(DOMAIN, self._device_id)},
"manufacturer": "Xiaomi",
"name": self._name,
"model": self._model,
}
if self._mac is not None:
device_info["connections"] = {(dr.CONNECTION_NETWORK_MAC, self._mac)}
return device_info

View File

@ -8,7 +8,7 @@
"data": {
"host": "[%key:common::config_flow::data::ip%]",
"token": "[%key:common::config_flow::data::api_token%]",
"name": "Name of the device"
"model": "Device model (Optional)"
}
}
},

View File

@ -6,7 +6,6 @@
},
"error": {
"cannot_connect": "Failed to connect",
"no_device_selected": "No device selected, please select one device.",
"unknown_device": "The device model is not known, not able to setup the device using config flow."
},
"flow_title": "Xiaomi Miio: {name}",
@ -14,27 +13,11 @@
"device": {
"data": {
"host": "IP Address",
"name": "Name of the device",
"token": "API Token"
"token": "API Token",
"model": "Device model (Optional)"
},
"description": "You will need the 32 character API Token, see https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token for instructions. Please note, that this API Token is different from the key used by the Xiaomi Aqara integration.",
"title": "Connect to a Xiaomi Miio Device or Xiaomi Gateway"
},
"gateway": {
"data": {
"host": "IP Address",
"name": "Name of the Gateway",
"token": "API Token"
},
"description": "You will need the 32 character API Token, see https://www.home-assistant.io/integrations/vacuum.xiaomi_miio/#retrieving-the-access-token for instructions. Please note, that this API Token is different from the key used by the Xiaomi Aqara integration.",
"title": "Connect to a Xiaomi Gateway"
},
"user": {
"data": {
"gateway": "Connect to a Xiaomi Gateway"
},
"description": "Select to which device you want to connect.",
"title": "Xiaomi Miio"
}
}
}

View File

@ -26,11 +26,15 @@ from homeassistant.components.vacuum import (
SUPPORT_STOP,
StateVacuumEntity,
)
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN, STATE_OFF, STATE_ON
from homeassistant.helpers import config_validation as cv, entity_platform
from homeassistant.util.dt import as_utc
from .const import (
CONF_DEVICE,
CONF_FLOW_TYPE,
DOMAIN,
SERVICE_CLEAN_SEGMENT,
SERVICE_CLEAN_ZONE,
SERVICE_GOTO,
@ -39,11 +43,11 @@ from .const import (
SERVICE_START_REMOTE_CONTROL,
SERVICE_STOP_REMOTE_CONTROL,
)
from .device import XiaomiMiioEntity
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "Xiaomi Vacuum cleaner"
DATA_KEY = "vacuum.xiaomi_miio"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
@ -116,110 +120,124 @@ STATE_CODE_TO_STATE = {
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Xiaomi vacuum cleaner robot platform."""
if DATA_KEY not in hass.data:
hass.data[DATA_KEY] = {}
host = config[CONF_HOST]
token = config[CONF_TOKEN]
name = config[CONF_NAME]
# Create handler
_LOGGER.info("Initializing with host %s (token %s...)", host, token[:5])
vacuum = Vacuum(host, token)
mirobo = MiroboVacuum(name, vacuum)
hass.data[DATA_KEY][host] = mirobo
async_add_entities([mirobo], update_before_add=True)
platform = entity_platform.current_platform.get()
platform.async_register_entity_service(
SERVICE_START_REMOTE_CONTROL,
{},
MiroboVacuum.async_remote_control_start.__name__,
"""Import Miio configuration from YAML."""
_LOGGER.warning(
"Loading Xiaomi Miio Vacuum via platform setup is deprecated. Please remove it from your configuration."
)
platform.async_register_entity_service(
SERVICE_STOP_REMOTE_CONTROL,
{},
MiroboVacuum.async_remote_control_stop.__name__,
)
platform.async_register_entity_service(
SERVICE_MOVE_REMOTE_CONTROL,
{
vol.Optional(ATTR_RC_VELOCITY): vol.All(
vol.Coerce(float), vol.Clamp(min=-0.29, max=0.29)
),
vol.Optional(ATTR_RC_ROTATION): vol.All(
vol.Coerce(int), vol.Clamp(min=-179, max=179)
),
vol.Optional(ATTR_RC_DURATION): cv.positive_int,
},
MiroboVacuum.async_remote_control_move.__name__,
)
platform.async_register_entity_service(
SERVICE_MOVE_REMOTE_CONTROL_STEP,
{
vol.Optional(ATTR_RC_VELOCITY): vol.All(
vol.Coerce(float), vol.Clamp(min=-0.29, max=0.29)
),
vol.Optional(ATTR_RC_ROTATION): vol.All(
vol.Coerce(int), vol.Clamp(min=-179, max=179)
),
vol.Optional(ATTR_RC_DURATION): cv.positive_int,
},
MiroboVacuum.async_remote_control_move_step.__name__,
)
platform.async_register_entity_service(
SERVICE_CLEAN_ZONE,
{
vol.Required(ATTR_ZONE_ARRAY): vol.All(
list,
[
vol.ExactSequence(
[
vol.Coerce(int),
vol.Coerce(int),
vol.Coerce(int),
vol.Coerce(int),
]
)
],
),
vol.Required(ATTR_ZONE_REPEATER): vol.All(
vol.Coerce(int), vol.Clamp(min=1, max=3)
),
},
MiroboVacuum.async_clean_zone.__name__,
)
platform.async_register_entity_service(
SERVICE_GOTO,
{
vol.Required("x_coord"): vol.Coerce(int),
vol.Required("y_coord"): vol.Coerce(int),
},
MiroboVacuum.async_goto.__name__,
)
platform.async_register_entity_service(
SERVICE_CLEAN_SEGMENT,
{vol.Required("segments"): vol.Any(vol.Coerce(int), [vol.Coerce(int)])},
MiroboVacuum.async_clean_segment.__name__,
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data=config,
)
)
class MiroboVacuum(StateVacuumEntity):
async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up the Xiaomi vacuum cleaner robot from a config entry."""
entities = []
if config_entry.data[CONF_FLOW_TYPE] == CONF_DEVICE:
host = config_entry.data[CONF_HOST]
token = config_entry.data[CONF_TOKEN]
name = config_entry.title
unique_id = config_entry.unique_id
# Create handler
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
vacuum = Vacuum(host, token)
mirobo = MiroboVacuum(name, vacuum, config_entry, unique_id)
entities.append(mirobo)
platform = entity_platform.current_platform.get()
platform.async_register_entity_service(
SERVICE_START_REMOTE_CONTROL,
{},
MiroboVacuum.async_remote_control_start.__name__,
)
platform.async_register_entity_service(
SERVICE_STOP_REMOTE_CONTROL,
{},
MiroboVacuum.async_remote_control_stop.__name__,
)
platform.async_register_entity_service(
SERVICE_MOVE_REMOTE_CONTROL,
{
vol.Optional(ATTR_RC_VELOCITY): vol.All(
vol.Coerce(float), vol.Clamp(min=-0.29, max=0.29)
),
vol.Optional(ATTR_RC_ROTATION): vol.All(
vol.Coerce(int), vol.Clamp(min=-179, max=179)
),
vol.Optional(ATTR_RC_DURATION): cv.positive_int,
},
MiroboVacuum.async_remote_control_move.__name__,
)
platform.async_register_entity_service(
SERVICE_MOVE_REMOTE_CONTROL_STEP,
{
vol.Optional(ATTR_RC_VELOCITY): vol.All(
vol.Coerce(float), vol.Clamp(min=-0.29, max=0.29)
),
vol.Optional(ATTR_RC_ROTATION): vol.All(
vol.Coerce(int), vol.Clamp(min=-179, max=179)
),
vol.Optional(ATTR_RC_DURATION): cv.positive_int,
},
MiroboVacuum.async_remote_control_move_step.__name__,
)
platform.async_register_entity_service(
SERVICE_CLEAN_ZONE,
{
vol.Required(ATTR_ZONE_ARRAY): vol.All(
list,
[
vol.ExactSequence(
[
vol.Coerce(int),
vol.Coerce(int),
vol.Coerce(int),
vol.Coerce(int),
]
)
],
),
vol.Required(ATTR_ZONE_REPEATER): vol.All(
vol.Coerce(int), vol.Clamp(min=1, max=3)
),
},
MiroboVacuum.async_clean_zone.__name__,
)
platform.async_register_entity_service(
SERVICE_GOTO,
{
vol.Required("x_coord"): vol.Coerce(int),
vol.Required("y_coord"): vol.Coerce(int),
},
MiroboVacuum.async_goto.__name__,
)
platform.async_register_entity_service(
SERVICE_CLEAN_SEGMENT,
{vol.Required("segments"): vol.Any(vol.Coerce(int), [vol.Coerce(int)])},
MiroboVacuum.async_clean_segment.__name__,
)
async_add_entities(entities, update_before_add=True)
class MiroboVacuum(XiaomiMiioEntity, StateVacuumEntity):
"""Representation of a Xiaomi Vacuum cleaner robot."""
def __init__(self, name, vacuum):
def __init__(self, name, device, entry, unique_id):
"""Initialize the Xiaomi vacuum cleaner robot handler."""
self._name = name
self._vacuum = vacuum
super().__init__(name, device, entry, unique_id)
self.vacuum_state = None
self._available = False
@ -233,11 +251,6 @@ class MiroboVacuum(StateVacuumEntity):
self._timers = None
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def state(self):
"""Return the status of the vacuum cleaner."""
@ -364,16 +377,16 @@ class MiroboVacuum(StateVacuumEntity):
async def async_start(self):
"""Start or resume the cleaning task."""
await self._try_command(
"Unable to start the vacuum: %s", self._vacuum.resume_or_start
"Unable to start the vacuum: %s", self._device.resume_or_start
)
async def async_pause(self):
"""Pause the cleaning task."""
await self._try_command("Unable to set start/pause: %s", self._vacuum.pause)
await self._try_command("Unable to set start/pause: %s", self._device.pause)
async def async_stop(self, **kwargs):
"""Stop the vacuum cleaner."""
await self._try_command("Unable to stop: %s", self._vacuum.stop)
await self._try_command("Unable to stop: %s", self._device.stop)
async def async_set_fan_speed(self, fan_speed, **kwargs):
"""Set fan speed."""
@ -390,28 +403,28 @@ class MiroboVacuum(StateVacuumEntity):
)
return
await self._try_command(
"Unable to set fan speed: %s", self._vacuum.set_fan_speed, fan_speed
"Unable to set fan speed: %s", self._device.set_fan_speed, fan_speed
)
async def async_return_to_base(self, **kwargs):
"""Set the vacuum cleaner to return to the dock."""
await self._try_command("Unable to return home: %s", self._vacuum.home)
await self._try_command("Unable to return home: %s", self._device.home)
async def async_clean_spot(self, **kwargs):
"""Perform a spot clean-up."""
await self._try_command(
"Unable to start the vacuum for a spot clean-up: %s", self._vacuum.spot
"Unable to start the vacuum for a spot clean-up: %s", self._device.spot
)
async def async_locate(self, **kwargs):
"""Locate the vacuum cleaner."""
await self._try_command("Unable to locate the botvac: %s", self._vacuum.find)
await self._try_command("Unable to locate the botvac: %s", self._device.find)
async def async_send_command(self, command, params=None, **kwargs):
"""Send raw command."""
await self._try_command(
"Unable to send command to the vacuum: %s",
self._vacuum.raw_command,
self._device.raw_command,
command,
params,
)
@ -419,13 +432,13 @@ class MiroboVacuum(StateVacuumEntity):
async def async_remote_control_start(self):
"""Start remote control mode."""
await self._try_command(
"Unable to start remote control the vacuum: %s", self._vacuum.manual_start
"Unable to start remote control the vacuum: %s", self._device.manual_start
)
async def async_remote_control_stop(self):
"""Stop remote control mode."""
await self._try_command(
"Unable to stop remote control the vacuum: %s", self._vacuum.manual_stop
"Unable to stop remote control the vacuum: %s", self._device.manual_stop
)
async def async_remote_control_move(
@ -434,7 +447,7 @@ class MiroboVacuum(StateVacuumEntity):
"""Move vacuum with remote control mode."""
await self._try_command(
"Unable to move with remote control the vacuum: %s",
self._vacuum.manual_control,
self._device.manual_control,
velocity=velocity,
rotation=rotation,
duration=duration,
@ -446,7 +459,7 @@ class MiroboVacuum(StateVacuumEntity):
"""Move vacuum one step with remote control mode."""
await self._try_command(
"Unable to remote control the vacuum: %s",
self._vacuum.manual_control_once,
self._device.manual_control_once,
velocity=velocity,
rotation=rotation,
duration=duration,
@ -456,7 +469,7 @@ class MiroboVacuum(StateVacuumEntity):
"""Goto the specified coordinates."""
await self._try_command(
"Unable to send the vacuum cleaner to the specified coordinates: %s",
self._vacuum.goto,
self._device.goto,
x_coord=x_coord,
y_coord=y_coord,
)
@ -468,23 +481,23 @@ class MiroboVacuum(StateVacuumEntity):
await self._try_command(
"Unable to start cleaning of the specified segments: %s",
self._vacuum.segment_clean,
self._device.segment_clean,
segments=segments,
)
def update(self):
"""Fetch state from the device."""
try:
state = self._vacuum.status()
state = self._device.status()
self.vacuum_state = state
self._fan_speeds = self._vacuum.fan_speed_presets()
self._fan_speeds = self._device.fan_speed_presets()
self._fan_speeds_reverse = {v: k for k, v in self._fan_speeds.items()}
self.consumable_state = self._vacuum.consumable_status()
self.clean_history = self._vacuum.clean_history()
self.last_clean = self._vacuum.last_clean_details()
self.dnd_state = self._vacuum.dnd_status()
self.consumable_state = self._device.consumable_status()
self.clean_history = self._device.clean_history()
self.last_clean = self._device.last_clean_details()
self.dnd_state = self._device.dnd_status()
self._available = True
except (OSError, DeviceException) as exc:
@ -494,7 +507,7 @@ class MiroboVacuum(StateVacuumEntity):
# Fetch timers separately, see #38285
try:
self._timers = self._vacuum.timer()
self._timers = self._device.timer()
except DeviceException as exc:
_LOGGER.debug(
"Unable to fetch timers, this may happen on some devices: %s", exc
@ -507,6 +520,6 @@ class MiroboVacuum(StateVacuumEntity):
_zone.append(repeats)
_LOGGER.debug("Zone with repeats: %s", zone)
try:
await self.hass.async_add_executor_job(self._vacuum.zoned_clean, zone)
await self.hass.async_add_executor_job(self._device.zoned_clean, zone)
except (OSError, DeviceException) as exc:
_LOGGER.error("Unable to send zoned_clean command to the vacuum: %s", exc)

View File

@ -257,6 +257,53 @@ async def test_import_flow_success(hass):
}
async def test_config_flow_step_device_manual_model_succes(hass):
"""Test config flow, device connection error, manual model."""
result = await hass.config_entries.flow.async_init(
const.DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == "form"
assert result["step_id"] == "device"
assert result["errors"] == {}
with patch(
"homeassistant.components.xiaomi_miio.device.Device.info",
side_effect=DeviceException({}),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: TEST_HOST, CONF_TOKEN: TEST_TOKEN},
)
assert result["type"] == "form"
assert result["step_id"] == "device"
assert result["errors"] == {"base": "cannot_connect"}
overwrite_model = const.MODELS_VACUUM[0]
with patch(
"homeassistant.components.xiaomi_miio.device.Device.info",
side_effect=DeviceException({}),
), patch(
"homeassistant.components.xiaomi_miio.async_setup_entry", return_value=True
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_TOKEN: TEST_TOKEN, const.CONF_MODEL: overwrite_model},
)
assert result["type"] == "create_entry"
assert result["title"] == DEFAULT_DEVICE_NAME
assert result["data"] == {
const.CONF_FLOW_TYPE: const.CONF_DEVICE,
CONF_HOST: TEST_HOST,
CONF_TOKEN: TEST_TOKEN,
const.CONF_MODEL: overwrite_model,
const.CONF_MAC: None,
}
async def config_flow_device_success(hass, model_to_test):
"""Test a successful config flow for a device (base class)."""
result = await hass.config_entries.flow.async_init(
@ -342,3 +389,16 @@ async def test_zeroconf_plug_success(hass):
test_plug_model = const.MODELS_SWITCH[0]
test_zeroconf_name = const.MODELS_SWITCH[0].replace(".", "-")
await zeroconf_device_success(hass, test_zeroconf_name, test_plug_model)
async def test_config_flow_vacuum_success(hass):
"""Test a successful config flow for a vacuum."""
test_vacuum_model = const.MODELS_VACUUM[0]
await config_flow_device_success(hass, test_vacuum_model)
async def test_zeroconf_vacuum_success(hass):
"""Test a successful zeroconf discovery of a vacuum."""
test_vacuum_model = const.MODELS_VACUUM[0]
test_zeroconf_name = const.MODELS_VACUUM[0].replace(".", "-")
await zeroconf_device_success(hass, test_zeroconf_name, test_vacuum_model)

View File

@ -22,6 +22,7 @@ from homeassistant.components.vacuum import (
STATE_CLEANING,
STATE_ERROR,
)
from homeassistant.components.xiaomi_miio import const
from homeassistant.components.xiaomi_miio.const import DOMAIN as XIAOMI_DOMAIN
from homeassistant.components.xiaomi_miio.vacuum import (
ATTR_CLEANED_AREA,
@ -38,7 +39,6 @@ from homeassistant.components.xiaomi_miio.vacuum import (
ATTR_SIDE_BRUSH_LEFT,
ATTR_TIMERS,
CONF_HOST,
CONF_NAME,
CONF_TOKEN,
SERVICE_CLEAN_SEGMENT,
SERVICE_CLEAN_ZONE,
@ -51,12 +51,14 @@ from homeassistant.components.xiaomi_miio.vacuum import (
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
CONF_PLATFORM,
STATE_OFF,
STATE_ON,
STATE_UNAVAILABLE,
)
from homeassistant.setup import async_setup_component
from .test_config_flow import TEST_MAC
from tests.common import MockConfigEntry
PLATFORM = "xiaomi_miio"
@ -521,17 +523,21 @@ async def setup_component(hass, entity_name):
"""Set up vacuum component."""
entity_id = f"{DOMAIN}.{entity_name}"
await async_setup_component(
hass,
DOMAIN,
{
DOMAIN: {
CONF_PLATFORM: PLATFORM,
CONF_HOST: "192.168.1.100",
CONF_NAME: entity_name,
CONF_TOKEN: "12345678901234567890123456789012",
}
config_entry = MockConfigEntry(
domain=XIAOMI_DOMAIN,
unique_id="123456",
title=entity_name,
data={
const.CONF_FLOW_TYPE: const.CONF_DEVICE,
CONF_HOST: "192.168.1.100",
CONF_TOKEN: "12345678901234567890123456789012",
const.CONF_MODEL: const.MODELS_VACUUM[0],
const.CONF_MAC: TEST_MAC,
},
)
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
return entity_id