mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 16:57:53 +00:00
Add Support for VeSync Devices - Outlets and Switches (#24953)
* Change dependency to pyvesync-v2 for vesync switch * async vesync component * FInish data_entry_flow * Update config flow * strings.json * Minor fix * Syntax fix * Minor Fixs * UI Fix * Minor Correct * Debug lines * fix device dictionaries * Light switch fix * Cleanup * pylint fixes * Hassfest and setup scripts * Flake8 fixes * Add vesync light platform * Fix typo * Update Devices Service * Fix update devices service * Add initial test * Add Config Flow Tests * Remove Extra Platforms * Fix requirements * Update pypi package * Add login to config_flow Avoid setting up component if login credentials are invalid * Fix variable import * Update config_flow.py * Update config_flow.py * Put VS object into hass.data instead of config entry * Update __init__.py * Handle Login Error * Fix invalid login error * Fix typo * Remove line * PEP fixes * Fix change requests * Fix typo * Update __init__.py * Update switch.py * Flake8 fix * Update test requirements * Fix permission * Address change requests * Address change requests * Fix device discovery indent, add MockConfigEntry * Fix vesynclightswitch classs * Remove active time attribute * Remove time_zone, grammar check
This commit is contained in:
parent
738d00fb05
commit
a8ec826ef7
@ -673,6 +673,9 @@ omit =
|
||||
homeassistant/components/venstar/climate.py
|
||||
homeassistant/components/vera/*
|
||||
homeassistant/components/verisure/*
|
||||
homeassistant/components/vesync/__init__.py
|
||||
homeassistant/components/vesync/common.py
|
||||
homeassistant/components/vesync/const.py
|
||||
homeassistant/components/vesync/switch.py
|
||||
homeassistant/components/viaggiatreno/sensor.py
|
||||
homeassistant/components/vizio/media_player.py
|
||||
|
@ -285,6 +285,7 @@ homeassistant/components/uptimerobot/* @ludeeus
|
||||
homeassistant/components/utility_meter/* @dgomes
|
||||
homeassistant/components/velux/* @Julius2342
|
||||
homeassistant/components/version/* @fabaff
|
||||
homeassistant/components/vesync/* @markperdue @webdjoe
|
||||
homeassistant/components/vizio/* @raman325
|
||||
homeassistant/components/vlc_telnet/* @rodripf
|
||||
homeassistant/components/waqi/* @andrey-git
|
||||
|
20
homeassistant/components/vesync/.translations/en.json
Normal file
20
homeassistant/components/vesync/.translations/en.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"config": {
|
||||
"title": "VeSync",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Enter Username and Password",
|
||||
"data": {
|
||||
"username": "Email Address",
|
||||
"password": "Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_login": "Invalid username or password"
|
||||
},
|
||||
"abort": {
|
||||
"already_setup": "Only one Vesync instance is allow"
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +1,114 @@
|
||||
"""The vesync component."""
|
||||
"""Etekcity VeSync integration."""
|
||||
import logging
|
||||
import voluptuous as vol
|
||||
from pyvesync import VeSync
|
||||
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from .common import async_process_devices
|
||||
from .config_flow import configured_instances
|
||||
from .const import (DOMAIN, VS_DISPATCHERS, VS_DISCOVERY, VS_SWITCHES,
|
||||
SERVICE_UPDATE_DEVS, VS_MANAGER)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
}),
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the VeSync component."""
|
||||
conf = config.get(DOMAIN)
|
||||
|
||||
if conf is None:
|
||||
return True
|
||||
|
||||
if not configured_instances(hass):
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={'source': SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_USERNAME: conf[CONF_USERNAME],
|
||||
CONF_PASSWORD: conf[CONF_PASSWORD]
|
||||
}))
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry):
|
||||
"""Set up Vesync as config entry."""
|
||||
username = config_entry.data[CONF_USERNAME]
|
||||
password = config_entry.data[CONF_PASSWORD]
|
||||
|
||||
time_zone = str(hass.config.time_zone)
|
||||
|
||||
manager = VeSync(username, password, time_zone)
|
||||
|
||||
login = await hass.async_add_executor_job(manager.login)
|
||||
|
||||
if not login:
|
||||
_LOGGER.error("Unable to login to the VeSync server")
|
||||
return False
|
||||
|
||||
device_dict = await async_process_devices(hass, manager)
|
||||
|
||||
forward_setup = hass.config_entries.async_forward_entry_setup
|
||||
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][VS_MANAGER] = manager
|
||||
|
||||
switches = hass.data[DOMAIN][VS_SWITCHES] = []
|
||||
|
||||
hass.data[DOMAIN][VS_DISPATCHERS] = []
|
||||
|
||||
if device_dict[VS_SWITCHES]:
|
||||
switches.extend(device_dict[VS_SWITCHES])
|
||||
hass.async_create_task(forward_setup(config_entry, 'switch'))
|
||||
|
||||
async def async_new_device_discovery(service):
|
||||
"""Discover if new devices should be added."""
|
||||
manager = hass.data[DOMAIN][VS_MANAGER]
|
||||
switches = hass.data[DOMAIN][VS_SWITCHES]
|
||||
|
||||
dev_dict = await async_process_devices(hass, manager)
|
||||
switch_devs = dev_dict.get(VS_SWITCHES, [])
|
||||
|
||||
switch_set = set(switch_devs)
|
||||
new_switches = list(switch_set.difference(switches))
|
||||
if new_switches and switches:
|
||||
switches.extend(new_switches)
|
||||
async_dispatcher_send(hass,
|
||||
VS_DISCOVERY.format(VS_SWITCHES),
|
||||
new_switches)
|
||||
return
|
||||
if new_switches and not switches:
|
||||
switches.extend(new_switches)
|
||||
hass.async_create_task(forward_setup(config_entry, 'switch'))
|
||||
|
||||
hass.services.async_register(DOMAIN,
|
||||
SERVICE_UPDATE_DEVS,
|
||||
async_new_device_discovery
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, entry):
|
||||
"""Unload a config entry."""
|
||||
forward_unload = hass.config_entries.async_forward_entry_unload
|
||||
remove_switches = False
|
||||
if hass.data[DOMAIN][VS_SWITCHES]:
|
||||
remove_switches = await forward_unload(entry, 'switch')
|
||||
|
||||
if remove_switches:
|
||||
hass.services.async_remove(DOMAIN, SERVICE_UPDATE_DEVS)
|
||||
del hass.data[DOMAIN]
|
||||
return True
|
||||
|
||||
return False
|
||||
|
70
homeassistant/components/vesync/common.py
Normal file
70
homeassistant/components/vesync/common.py
Normal file
@ -0,0 +1,70 @@
|
||||
"""Common utilities for VeSync Component."""
|
||||
import logging
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
from .const import VS_SWITCHES
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_process_devices(hass, manager):
|
||||
"""Assign devices to proper component."""
|
||||
devices = {}
|
||||
devices[VS_SWITCHES] = []
|
||||
|
||||
await hass.async_add_executor_job(manager.update)
|
||||
|
||||
if manager.outlets:
|
||||
devices[VS_SWITCHES].extend(manager.outlets)
|
||||
_LOGGER.info("%d VeSync outlets found", len(manager.outlets))
|
||||
|
||||
if manager.switches:
|
||||
for switch in manager.switches:
|
||||
if not switch.is_dimmable():
|
||||
devices[VS_SWITCHES].append(switch)
|
||||
_LOGGER.info(
|
||||
"%d VeSync standard switches found", len(manager.switches))
|
||||
|
||||
return devices
|
||||
|
||||
|
||||
class VeSyncDevice(ToggleEntity):
|
||||
"""Base class for VeSync Device Representations."""
|
||||
|
||||
def __init__(self, device):
|
||||
"""Initialize the VeSync device."""
|
||||
self.device = device
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the ID of this device."""
|
||||
if isinstance(self.device.sub_device_no, int):
|
||||
return ('{}{}'.format(
|
||||
self.device.cid, str(self.device.sub_device_no)))
|
||||
return self.device.cid
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self.device.device_name
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return True if switch is on."""
|
||||
return self.device.device_status == "on"
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if device is available."""
|
||||
return self.device.connection_status == "online"
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the device on."""
|
||||
self.device.turn_on()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the device off."""
|
||||
self.device.turn_off()
|
||||
|
||||
def update(self):
|
||||
"""Update vesync device."""
|
||||
self.device.update()
|
70
homeassistant/components/vesync/config_flow.py
Normal file
70
homeassistant/components/vesync/config_flow.py
Normal file
@ -0,0 +1,70 @@
|
||||
"""Config flow utilities."""
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
import voluptuous as vol
|
||||
from pyvesync import VeSync
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@callback
|
||||
def configured_instances(hass):
|
||||
"""Return already configured instances."""
|
||||
return hass.config_entries.async_entries(DOMAIN)
|
||||
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class VeSyncFlowHandler(config_entries.ConfigFlow):
|
||||
"""Handle a config flow."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||
|
||||
def __init__(self):
|
||||
"""Instantiate config flow."""
|
||||
self._username = None
|
||||
self._password = None
|
||||
self.data_schema = OrderedDict()
|
||||
self.data_schema[vol.Required(CONF_USERNAME)] = str
|
||||
self.data_schema[vol.Required(CONF_PASSWORD)] = str
|
||||
|
||||
@callback
|
||||
def _show_form(self, errors=None):
|
||||
"""Show form to the user."""
|
||||
return self.async_show_form(
|
||||
step_id='user',
|
||||
data_schema=vol.Schema(self.data_schema),
|
||||
errors=errors if errors else {},
|
||||
)
|
||||
|
||||
async def async_step_import(self, import_config):
|
||||
"""Handle external yaml configuration."""
|
||||
return await self.async_step_user(import_config)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow start."""
|
||||
if configured_instances(self.hass):
|
||||
return self.async_abort(reason='already_setup')
|
||||
|
||||
if not user_input:
|
||||
return self._show_form()
|
||||
|
||||
self._username = user_input[CONF_USERNAME]
|
||||
self._password = user_input[CONF_PASSWORD]
|
||||
|
||||
manager = VeSync(self._username, self._password)
|
||||
login = await self.hass.async_add_executor_job(manager.login)
|
||||
if not login:
|
||||
return self._show_form(errors={'base': 'invalid_login'})
|
||||
|
||||
return self.async_create_entry(
|
||||
title=self._username,
|
||||
data={
|
||||
CONF_USERNAME: self._username,
|
||||
CONF_PASSWORD: self._password,
|
||||
},
|
||||
)
|
9
homeassistant/components/vesync/const.py
Normal file
9
homeassistant/components/vesync/const.py
Normal file
@ -0,0 +1,9 @@
|
||||
"""Constants for VeSync Component."""
|
||||
|
||||
DOMAIN = 'vesync'
|
||||
VS_DISPATCHERS = 'vesync_dispatchers'
|
||||
VS_DISCOVERY = 'vesync_discovery_{}'
|
||||
SERVICE_UPDATE_DEVS = 'update_devices'
|
||||
|
||||
VS_SWITCHES = 'switches'
|
||||
VS_MANAGER = 'manager'
|
@ -1,10 +1,9 @@
|
||||
{
|
||||
"domain": "vesync",
|
||||
"name": "Vesync",
|
||||
"name": "VeSync",
|
||||
"documentation": "https://www.home-assistant.io/components/vesync",
|
||||
"requirements": [
|
||||
"pyvesync_v2==0.9.7"
|
||||
],
|
||||
"dependencies": [],
|
||||
"codeowners": []
|
||||
"codeowners": ["@markperdue", "@webdjoe"],
|
||||
"requirements": ["pyvesync==1.1.0"],
|
||||
"config_flow": true
|
||||
}
|
||||
|
2
homeassistant/components/vesync/services.yaml
Normal file
2
homeassistant/components/vesync/services.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
update_devices:
|
||||
description: Add new VeSync devices to Home Assistant
|
20
homeassistant/components/vesync/strings.json
Normal file
20
homeassistant/components/vesync/strings.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"config": {
|
||||
"title": "VeSync",
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Enter Username and Password",
|
||||
"data": {
|
||||
"username": "Email Address",
|
||||
"password": "Password"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_login": "Invalid username or password"
|
||||
},
|
||||
"abort": {
|
||||
"already_setup": "Only one Vesync instance is allowed"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,102 +1,100 @@
|
||||
"""Support for Etekcity VeSync switches."""
|
||||
import logging
|
||||
import voluptuous as vol
|
||||
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA)
|
||||
from homeassistant.const import (CONF_USERNAME, CONF_PASSWORD)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.components.switch import SwitchDevice
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from .const import VS_DISCOVERY, VS_DISPATCHERS, VS_SWITCHES, DOMAIN
|
||||
from .common import VeSyncDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
})
|
||||
DEV_TYPE_TO_HA = {
|
||||
'wifi-switch-1.3': 'outlet',
|
||||
'ESW03-USA': 'outlet',
|
||||
'ESW01-EU': 'outlet',
|
||||
'ESW15-USA': 'outlet',
|
||||
'ESWL01': 'switch',
|
||||
'ESWL03': 'switch',
|
||||
'ESO15-TB': 'outlet'
|
||||
}
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the VeSync switch platform."""
|
||||
from pyvesync_v2.vesync import VeSync
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up switches."""
|
||||
async def async_discover(devices):
|
||||
"""Add new devices to platform."""
|
||||
_async_setup_entities(devices, async_add_entities)
|
||||
|
||||
switches = []
|
||||
disp = async_dispatcher_connect(
|
||||
hass, VS_DISCOVERY.format(VS_SWITCHES), async_discover)
|
||||
hass.data[DOMAIN][VS_DISPATCHERS].append(disp)
|
||||
|
||||
manager = VeSync(config.get(CONF_USERNAME), config.get(CONF_PASSWORD))
|
||||
_async_setup_entities(
|
||||
hass.data[DOMAIN][VS_SWITCHES],
|
||||
async_add_entities
|
||||
)
|
||||
return True
|
||||
|
||||
if not manager.login():
|
||||
_LOGGER.error("Unable to login to VeSync")
|
||||
return
|
||||
|
||||
manager.update()
|
||||
|
||||
if manager.devices is not None and manager.devices:
|
||||
if len(manager.devices) == 1:
|
||||
count_string = 'switch'
|
||||
@callback
|
||||
def _async_setup_entities(devices, async_add_entities):
|
||||
"""Check if device is online and add entity."""
|
||||
dev_list = []
|
||||
for dev in devices:
|
||||
if DEV_TYPE_TO_HA.get(dev.device_type) == 'outlet':
|
||||
dev_list.append(VeSyncSwitchHA(dev))
|
||||
elif DEV_TYPE_TO_HA.get(dev.device_type) == 'switch':
|
||||
dev_list.append(VeSyncLightSwitch(dev))
|
||||
else:
|
||||
count_string = 'switches'
|
||||
_LOGGER.warning("%s - Unkown device type - %s",
|
||||
dev.device_name, dev.device_type)
|
||||
continue
|
||||
|
||||
_LOGGER.info("Discovered %d VeSync %s",
|
||||
len(manager.devices), count_string)
|
||||
|
||||
for switch in manager.devices:
|
||||
switches.append(VeSyncSwitchHA(switch))
|
||||
_LOGGER.info("Added a VeSync switch named '%s'",
|
||||
switch.device_name)
|
||||
else:
|
||||
_LOGGER.info("No VeSync devices found")
|
||||
|
||||
add_entities(switches)
|
||||
async_add_entities(
|
||||
dev_list,
|
||||
update_before_add=True
|
||||
)
|
||||
|
||||
|
||||
class VeSyncSwitchHA(SwitchDevice):
|
||||
class VeSyncSwitchHA(VeSyncDevice, SwitchDevice):
|
||||
"""Representation of a VeSync switch."""
|
||||
|
||||
def __init__(self, plug):
|
||||
"""Initialize the VeSync switch device."""
|
||||
super().__init__(plug)
|
||||
self.smartplug = plug
|
||||
self._current_power_w = None
|
||||
self._today_energy_kwh = None
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the ID of this switch."""
|
||||
return self.smartplug.cid
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the switch."""
|
||||
return self.smartplug.device_name
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the device."""
|
||||
attr = {}
|
||||
if hasattr(self.smartplug, 'weekly_energy_total'):
|
||||
attr['voltage'] = self.smartplug.voltage
|
||||
attr['weekly_energy_total'] = self.smartplug.weekly_energy_total
|
||||
attr['monthly_energy_total'] = self.smartplug.monthly_energy_total
|
||||
attr['yearly_energy_total'] = self.smartplug.yearly_energy_total
|
||||
return attr
|
||||
|
||||
@property
|
||||
def current_power_w(self):
|
||||
"""Return the current power usage in W."""
|
||||
return self._current_power_w
|
||||
return self.smartplug.power
|
||||
|
||||
@property
|
||||
def today_energy_kwh(self):
|
||||
"""Return the today total energy usage in kWh."""
|
||||
return self._today_energy_kwh
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return True if switch is available."""
|
||||
return self.smartplug.connection_status == "online"
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return True if switch is on."""
|
||||
return self.smartplug.device_status == "on"
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the switch on."""
|
||||
self.smartplug.turn_on()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the switch off."""
|
||||
self.smartplug.turn_off()
|
||||
return self.smartplug.energy_today
|
||||
|
||||
def update(self):
|
||||
"""Handle data changes for node values."""
|
||||
"""Update outlet details and energy usage."""
|
||||
self.smartplug.update()
|
||||
if self.smartplug.devtype == 'outlet':
|
||||
self._current_power_w = self.smartplug.get_power()
|
||||
self._today_energy_kwh = self.smartplug.get_kwh_today()
|
||||
self.smartplug.update_energy()
|
||||
|
||||
|
||||
class VeSyncLightSwitch(VeSyncDevice, SwitchDevice):
|
||||
"""Handle representation of VeSync Light Switch."""
|
||||
|
||||
def __init__(self, switch):
|
||||
"""Initialize Light Switch device class."""
|
||||
super().__init__(switch)
|
||||
self.switch = switch
|
||||
|
@ -56,6 +56,7 @@ FLOWS = [
|
||||
"twilio",
|
||||
"unifi",
|
||||
"upnp",
|
||||
"vesync",
|
||||
"wemo",
|
||||
"wwlln",
|
||||
"zha",
|
||||
|
@ -1559,7 +1559,7 @@ pyuptimerobot==0.0.5
|
||||
pyvera==0.3.2
|
||||
|
||||
# homeassistant.components.vesync
|
||||
pyvesync_v2==0.9.7
|
||||
pyvesync==1.1.0
|
||||
|
||||
# homeassistant.components.vizio
|
||||
pyvizio==0.0.7
|
||||
|
@ -315,6 +315,9 @@ python_awair==0.0.4
|
||||
# homeassistant.components.tradfri
|
||||
pytradfri[async]==6.0.1
|
||||
|
||||
# homeassistant.components.vesync
|
||||
pyvesync==1.1.0
|
||||
|
||||
# homeassistant.components.html5
|
||||
pywebpush==1.9.2
|
||||
|
||||
|
@ -132,6 +132,7 @@ TEST_REQUIREMENTS = (
|
||||
'pytradfri[async]',
|
||||
'pyunifi',
|
||||
'pyupnp-async',
|
||||
'pyvesync',
|
||||
'pywebpush',
|
||||
'pyHS100',
|
||||
'PyNaCl',
|
||||
|
1
tests/components/vesync/__init__.py
Normal file
1
tests/components/vesync/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""Tests for VeSync Component."""
|
57
tests/components/vesync/test_config_flow.py
Normal file
57
tests/components/vesync/test_config_flow.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""Test for vesync config flow."""
|
||||
from unittest.mock import patch
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.components.vesync import config_flow, DOMAIN
|
||||
from homeassistant.const import CONF_USERNAME, CONF_PASSWORD
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_abort_already_setup(hass):
|
||||
"""Test if we abort because component is already setup."""
|
||||
flow = config_flow.VeSyncFlowHandler()
|
||||
flow.hass = hass
|
||||
MockConfigEntry(
|
||||
domain=DOMAIN, title='user', data={'user': 'pass'}
|
||||
).add_to_hass(hass)
|
||||
result = await flow.async_step_user()
|
||||
|
||||
assert result['type'] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result['reason'] == 'already_setup'
|
||||
|
||||
|
||||
async def test_invalid_login_error(hass):
|
||||
"""Test if we return error for invalid username and password."""
|
||||
test_dict = {CONF_USERNAME: 'user', CONF_PASSWORD: 'pass'}
|
||||
flow = config_flow.VeSyncFlowHandler()
|
||||
flow.hass = hass
|
||||
with patch('pyvesync.vesync.VeSync.login', return_value=False):
|
||||
result = await flow.async_step_user(user_input=test_dict)
|
||||
|
||||
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result['errors'] == {'base': 'invalid_login'}
|
||||
|
||||
|
||||
async def test_config_flow_configuration_yaml(hass):
|
||||
"""Test config flow with configuration.yaml user input."""
|
||||
test_dict = {CONF_USERNAME: 'user', CONF_PASSWORD: 'pass'}
|
||||
flow = config_flow.VeSyncFlowHandler()
|
||||
flow.hass = hass
|
||||
with patch('pyvesync.vesync.VeSync.login', return_value=True):
|
||||
result = await flow.async_step_import(test_dict)
|
||||
|
||||
assert result['data'].get(CONF_USERNAME) == test_dict[CONF_USERNAME]
|
||||
assert result['data'].get(CONF_PASSWORD) == test_dict[CONF_PASSWORD]
|
||||
|
||||
|
||||
async def test_config_flow_user_input(hass):
|
||||
"""Test config flow with user input."""
|
||||
flow = config_flow.VeSyncFlowHandler()
|
||||
flow.hass = hass
|
||||
result = await flow.async_step_user()
|
||||
assert result['type'] == data_entry_flow.RESULT_TYPE_FORM
|
||||
with patch('pyvesync.vesync.VeSync.login', return_value=True):
|
||||
result = await flow.async_step_user(
|
||||
{CONF_USERNAME: 'user', CONF_PASSWORD: 'pass'})
|
||||
assert result['type'] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result['data'][CONF_USERNAME] == 'user'
|
||||
assert result['data'][CONF_PASSWORD] == 'pass'
|
Loading…
x
Reference in New Issue
Block a user