Add Mill config flow (#35136)

* Mill config, wip

* add tests

* fix merge

* update tests

* unique id

* Mill strings

* mill config flow

* mill config flow

* test import

* test import

* req

* ccoverage

* Apply suggestions from code review

Co-authored-by: J. Nick Koston <nick@koston.org>

* style

* Apply suggestions from code review

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* update strings

* add test

Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
pull/35460/head
Daniel Høyer Iversen 2020-05-10 15:44:05 +02:00 committed by GitHub
parent 8050d8555e
commit f302c6fd65
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 213 additions and 11 deletions

View File

@ -452,6 +452,7 @@ omit =
homeassistant/components/miflora/sensor.py
homeassistant/components/mikrotik/hub.py
homeassistant/components/mikrotik/device_tracker.py
homeassistant/components/mill/__init__.py
homeassistant/components/mill/climate.py
homeassistant/components/mill/const.py
homeassistant/components/minecraft_server/__init__.py

View File

@ -1 +1,25 @@
"""The mill component."""
import logging
_LOGGER = logging.getLogger(__name__)
async def async_setup(hass, config):
"""Set up the Mill platform."""
return True
async def async_setup_entry(hass, entry):
"""Set up the Mill heater."""
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, "climate")
)
return True
async def async_unload_entry(hass, config_entry):
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_forward_entry_unload(
config_entry, "climate"
)
return unload_ok

View File

@ -4,7 +4,7 @@ import logging
from mill import Mill
import voluptuous as vol
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
CURRENT_HVAC_HEAT,
CURRENT_HVAC_IDLE,
@ -20,6 +20,7 @@ from homeassistant.const import (
CONF_USERNAME,
TEMP_CELSIUS,
)
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -29,6 +30,7 @@ from .const import (
ATTR_ROOM_NAME,
ATTR_SLEEP_TEMP,
DOMAIN,
MANUFACTURER,
MAX_TEMP,
MIN_TEMP,
SERVICE_SET_ROOM_TEMP,
@ -38,10 +40,6 @@ _LOGGER = logging.getLogger(__name__)
SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_PASSWORD): cv.string}
)
SET_ROOM_TEMP_SCHEMA = vol.Schema(
{
vol.Required(ATTR_ROOM_NAME): cv.string,
@ -52,16 +50,15 @@ SET_ROOM_TEMP_SCHEMA = vol.Schema(
)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the Mill heater."""
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up the Mill climate."""
mill_data_connection = Mill(
config[CONF_USERNAME],
config[CONF_PASSWORD],
entry.data[CONF_USERNAME],
entry.data[CONF_PASSWORD],
websession=async_get_clientsession(hass),
)
if not await mill_data_connection.connect():
_LOGGER.error("Failed to connect to Mill")
return
raise ConfigEntryNotReady
await mill_data_connection.find_all_heaters()
@ -218,3 +215,19 @@ class MillHeater(ClimateEntity):
async def async_update(self):
"""Retrieve latest state."""
self._heater = await self._conn.update_device(self._heater.device_id)
@property
def device_id(self):
"""Return the ID of the physical device this sensor is part of."""
return self._heater.device_id
@property
def device_info(self):
"""Return the device_info of the device."""
device_info = {
"identifiers": {(DOMAIN, self.device_id)},
"name": self.name,
"manufacturer": MANUFACTURER,
"model": f"generation {1 if self._heater.is_gen1 else 2}",
}
return device_info

View File

@ -0,0 +1,55 @@
"""Adds config flow for Mill integration."""
import logging
from mill import Mill
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import DOMAIN # pylint:disable=unused-import
_LOGGER = logging.getLogger(__name__)
DATA_SCHEMA = vol.Schema(
{vol.Required(CONF_USERNAME): str, vol.Required(CONF_PASSWORD): str}
)
class MillConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Mill integration."""
VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
if user_input is None:
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors={},
)
username = user_input[CONF_USERNAME].replace(" ", "")
password = user_input[CONF_PASSWORD].replace(" ", "")
mill_data_connection = Mill(
username, password, websession=async_get_clientsession(self.hass),
)
errors = {}
if not await mill_data_connection.connect():
errors["connection_error"] = "connection_error"
return self.async_show_form(
step_id="user", data_schema=DATA_SCHEMA, errors=errors,
)
unique_id = username
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured()
return self.async_create_entry(
title=unique_id, data={CONF_USERNAME: username, CONF_PASSWORD: password},
)

View File

@ -4,6 +4,7 @@ ATTR_AWAY_TEMP = "away_temp"
ATTR_COMFORT_TEMP = "comfort_temp"
ATTR_ROOM_NAME = "room_name"
ATTR_SLEEP_TEMP = "sleep_temp"
MANUFACTURER = "Mill"
MAX_TEMP = 35
MIN_TEMP = 5
DOMAIN = "mill"

View File

@ -0,0 +1,18 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
},
"error": {
"connection_error": "[%key:common::config_flow::error::cannot_connect%]"
},
"step": {
"user": {
"data": {
"username": "[%key:common::config_flow::data::username%]",
"password": "[%key:common::config_flow::data::password%]"
}
}
}
}
}

View File

@ -373,6 +373,9 @@ meteofrance==0.3.7
# homeassistant.components.mfi
mficlient==0.3.0
# homeassistant.components.mill
millheater==0.3.4
# homeassistant.components.minio
minio==4.0.9

View File

@ -0,0 +1 @@
"""Tests for Mill."""

View File

@ -0,0 +1,86 @@
"""Tests for Mill config flow."""
import pytest
from homeassistant.components.mill.const import DOMAIN
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from tests.async_mock import patch
from tests.common import MockConfigEntry
@pytest.fixture(name="mill_setup", autouse=True)
def mill_setup_fixture():
"""Patch mill setup entry."""
with patch("homeassistant.components.mill.async_setup_entry", return_value=True):
yield
async def test_show_config_form(hass):
"""Test show configuration form."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
async def test_create_entry(hass):
"""Test create entry from user input."""
test_data = {
CONF_USERNAME: "user",
CONF_PASSWORD: "pswd",
}
with patch("mill.Mill.connect", return_value=True):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=test_data
)
assert result["type"] == "create_entry"
assert result["title"] == test_data[CONF_USERNAME]
assert result["data"] == test_data
async def test_flow_entry_already_exists(hass):
"""Test user input for config_entry that already exists."""
test_data = {
CONF_USERNAME: "user",
CONF_PASSWORD: "pswd",
}
first_entry = MockConfigEntry(
domain="mill", data=test_data, unique_id=test_data[CONF_USERNAME],
)
first_entry.add_to_hass(hass)
with patch("mill.Mill.connect", return_value=True):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=test_data
)
assert result["type"] == "abort"
assert result["reason"] == "already_configured"
async def test_connection_error(hass):
"""Test connection error."""
test_data = {
CONF_USERNAME: "user",
CONF_PASSWORD: "pswd",
}
first_entry = MockConfigEntry(
domain="mill", data=test_data, unique_id=test_data[CONF_USERNAME],
)
first_entry.add_to_hass(hass)
with patch("mill.Mill.connect", return_value=False):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": "user"}, data=test_data
)
assert result["type"] == "form"
assert result["errors"]["connection_error"] == "connection_error"