Add config flow for One wire (#39321)
* Add support for config_flow Add support for switches Add support for binary sensors * Add config flow strings * Update .coveragerc * black-formatting * fixes for isort and flake * fixes for pylint * fixes for isort * fixes for isort * fixes for config_flow * Add devices to Device Registry * Updated comments * fixes for flake8 * Updated comments * Updated comments * Implement async_unload_entry * remove binary_sensor and switch implementation (will move to new PR) * Update .coveragerc Co-authored-by: Chris Talkington <chris@talkingtontech.com> * Update .coveragerc * Review config flow to store the configuration type * Add config_flow tests * Move CONF_NAMES to constants * Fix isort * Tweak to onewire logger * Tweak to onewire logger for sensor * Reset _LOGGER variable * Normalise header * Normalise header * Update to use references in config flow translations * Improve test coverage * fixes for isort * Update async_unload_entry * Update common strings * Update imports * Move connect attempt to executor * Update common strings * Remove OWFS from config_flow * Prevent duplicate config entries * Fix isort * Fix flake8 * Fix tests following removal of OWFS implementation * Fix flake8 * Adjust config from yaml to config-flow, and add ability to specify sysbus directory * Ajust unique_id for config entries * Fix test_config_flow * Fix invalid merge * Convert yaml to config_entry * Update sysbus tests * Tweaks to yaml import process, and add OWFS warning * Enable migration of OWFS platform config to config_entry * update the existing corresponding config entry on OWFS conversion * Remove CONFIG_SCHEMA * Move data_schema to constants * Remove log message * Remove duplicate warning * Update already_configured to use already_configured_device constant * Schedule get_entities on the executor * Update duplicate entry check for OWServer * Review TryCatch * Schedule os.path.isdir on the executor * rename owhost/owport * Update checks for empty * Fix incorrect patch * Move config_flow validation methods to new OneWireHub * Fix typo and pre-commit * Cleanup try/else * patch async_setup/async_setup_entry * cleanup implicit exit point * Fix invalid patch * cleanup implicit exit point * cleanup implicit exit point Co-authored-by: Chris Talkington <chris@talkingtontech.com>
This commit is contained in:
parent
ed232ac733
commit
6ef53a2c5d
14 changed files with 774 additions and 32 deletions
|
@ -1 +1,31 @@
|
|||
"""The 1-Wire component."""
|
||||
import asyncio
|
||||
|
||||
from .const import SUPPORTED_PLATFORMS
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up 1-Wire integrations."""
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry):
|
||||
"""Set up a 1-Wire proxy for a config entry."""
|
||||
for component in SUPPORTED_PLATFORMS:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(config_entry, component)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, config_entry):
|
||||
"""Unload a config entry."""
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
hass.config_entries.async_forward_entry_unload(config_entry, component)
|
||||
for component in SUPPORTED_PLATFORMS
|
||||
]
|
||||
)
|
||||
)
|
||||
return unload_ok
|
||||
|
|
195
homeassistant/components/onewire/config_flow.py
Normal file
195
homeassistant/components/onewire/config_flow.py
Normal file
|
@ -0,0 +1,195 @@
|
|||
"""Config flow for 1-Wire component."""
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import exceptions
|
||||
from homeassistant.config_entries import CONN_CLASS_LOCAL_POLL, ConfigFlow
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
from .const import ( # pylint: disable=unused-import
|
||||
CONF_MOUNT_DIR,
|
||||
CONF_TYPE_OWFS,
|
||||
CONF_TYPE_OWSERVER,
|
||||
CONF_TYPE_SYSBUS,
|
||||
DEFAULT_OWSERVER_HOST,
|
||||
DEFAULT_OWSERVER_PORT,
|
||||
DEFAULT_SYSBUS_MOUNT_DIR,
|
||||
DOMAIN,
|
||||
)
|
||||
from .onewirehub import OneWireHub
|
||||
|
||||
DATA_SCHEMA_USER = vol.Schema(
|
||||
{vol.Required(CONF_TYPE): vol.In([CONF_TYPE_OWSERVER, CONF_TYPE_SYSBUS])}
|
||||
)
|
||||
DATA_SCHEMA_OWSERVER = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST, default=DEFAULT_OWSERVER_HOST): str,
|
||||
vol.Required(CONF_PORT, default=DEFAULT_OWSERVER_PORT): int,
|
||||
}
|
||||
)
|
||||
DATA_SCHEMA_MOUNTDIR = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_MOUNT_DIR, default=DEFAULT_SYSBUS_MOUNT_DIR): str,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def validate_input_owserver(hass: HomeAssistantType, data):
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
Data has the keys from DATA_SCHEMA_OWSERVER with values provided by the user.
|
||||
"""
|
||||
|
||||
hub = OneWireHub(hass)
|
||||
|
||||
host = data[CONF_HOST]
|
||||
port = data[CONF_PORT]
|
||||
if not await hub.can_connect(host, port):
|
||||
raise CannotConnect
|
||||
|
||||
# Return info that you want to store in the config entry.
|
||||
return {"title": host}
|
||||
|
||||
|
||||
def is_duplicate_owserver_entry(hass: HomeAssistantType, user_input):
|
||||
"""Check existing entries for matching host and port."""
|
||||
for config_entry in hass.config_entries.async_entries(DOMAIN):
|
||||
if (
|
||||
config_entry.data[CONF_TYPE] == CONF_TYPE_OWSERVER
|
||||
and config_entry.data[CONF_HOST] == user_input[CONF_HOST]
|
||||
and config_entry.data[CONF_PORT] == str(user_input[CONF_PORT])
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
async def validate_input_mount_dir(hass: HomeAssistantType, data):
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
Data has the keys from DATA_SCHEMA_MOUNTDIR with values provided by the user.
|
||||
"""
|
||||
hub = OneWireHub(hass)
|
||||
|
||||
mount_dir = data[CONF_MOUNT_DIR]
|
||||
if not await hub.is_valid_mount_dir(mount_dir):
|
||||
raise InvalidPath
|
||||
|
||||
# Return info that you want to store in the config entry.
|
||||
return {"title": mount_dir}
|
||||
|
||||
|
||||
class OneWireFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle 1-Wire config flow."""
|
||||
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = CONN_CLASS_LOCAL_POLL
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize 1-Wire config flow."""
|
||||
self.onewire_config = {}
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle 1-Wire config flow start.
|
||||
|
||||
Let user manually input configuration.
|
||||
"""
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
self.onewire_config.update(user_input)
|
||||
if CONF_TYPE_OWSERVER == user_input[CONF_TYPE]:
|
||||
return await self.async_step_owserver()
|
||||
if CONF_TYPE_SYSBUS == user_input[CONF_TYPE]:
|
||||
return await self.async_step_mount_dir()
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=DATA_SCHEMA_USER,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_owserver(self, user_input=None):
|
||||
"""Handle OWServer configuration."""
|
||||
errors = {}
|
||||
if user_input:
|
||||
# Prevent duplicate entries
|
||||
if is_duplicate_owserver_entry(self.hass, user_input):
|
||||
return self.async_abort(reason="already_configured")
|
||||
|
||||
self.onewire_config.update(user_input)
|
||||
|
||||
try:
|
||||
info = await validate_input_owserver(self.hass, user_input)
|
||||
except CannotConnect:
|
||||
errors["base"] = "cannot_connect"
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title=info["title"], data=self.onewire_config
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="owserver",
|
||||
data_schema=DATA_SCHEMA_OWSERVER,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_mount_dir(self, user_input=None):
|
||||
"""Handle SysBus configuration."""
|
||||
errors = {}
|
||||
if user_input:
|
||||
# Prevent duplicate entries
|
||||
await self.async_set_unique_id(
|
||||
f"{CONF_TYPE_SYSBUS}:{user_input[CONF_MOUNT_DIR]}"
|
||||
)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
self.onewire_config.update(user_input)
|
||||
|
||||
try:
|
||||
info = await validate_input_mount_dir(self.hass, user_input)
|
||||
except InvalidPath:
|
||||
errors["base"] = "invalid_path"
|
||||
else:
|
||||
return self.async_create_entry(
|
||||
title=info["title"], data=self.onewire_config
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="mount_dir",
|
||||
data_schema=DATA_SCHEMA_MOUNTDIR,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_import(self, platform_config):
|
||||
"""Handle import configuration from YAML."""
|
||||
# OWServer
|
||||
if platform_config[CONF_TYPE] == CONF_TYPE_OWSERVER:
|
||||
if CONF_PORT not in platform_config:
|
||||
platform_config[CONF_PORT] = DEFAULT_OWSERVER_PORT
|
||||
return await self.async_step_owserver(platform_config)
|
||||
|
||||
# OWFS
|
||||
if platform_config[CONF_TYPE] == CONF_TYPE_OWFS: # pragma: no cover
|
||||
# This part of the implementation does not conform to policy regarding 3rd-party libraries, and will not longer be updated.
|
||||
# https://developers.home-assistant.io/docs/creating_platform_code_review/#5-communication-with-devicesservices
|
||||
await self.async_set_unique_id(
|
||||
f"{CONF_TYPE_OWFS}:{platform_config[CONF_MOUNT_DIR]}"
|
||||
)
|
||||
self._abort_if_unique_id_configured(
|
||||
updates=platform_config, reload_on_update=True
|
||||
)
|
||||
return self.async_create_entry(
|
||||
title=platform_config[CONF_MOUNT_DIR], data=platform_config
|
||||
)
|
||||
|
||||
# SysBus
|
||||
if CONF_MOUNT_DIR not in platform_config:
|
||||
platform_config[CONF_MOUNT_DIR] = DEFAULT_SYSBUS_MOUNT_DIR
|
||||
return await self.async_step_mount_dir(platform_config)
|
||||
|
||||
|
||||
class CannotConnect(exceptions.HomeAssistantError):
|
||||
"""Error to indicate we cannot connect."""
|
||||
|
||||
|
||||
class InvalidPath(exceptions.HomeAssistantError):
|
||||
"""Error to indicate the path is invalid."""
|
|
@ -8,6 +8,7 @@ CONF_TYPE_OWFS = "OWFS"
|
|||
CONF_TYPE_OWSERVER = "OWServer"
|
||||
CONF_TYPE_SYSBUS = "SysBus"
|
||||
|
||||
DEFAULT_OWSERVER_HOST = "localhost"
|
||||
DEFAULT_OWSERVER_PORT = 4304
|
||||
DEFAULT_SYSBUS_MOUNT_DIR = "/sys/bus/w1/devices/"
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"domain": "onewire",
|
||||
"name": "1-Wire",
|
||||
"documentation": "https://www.home-assistant.io/integrations/onewire",
|
||||
"config_flow": true,
|
||||
"requirements": ["pyownet==0.10.0.post1", "pi1wire==0.1.0"],
|
||||
"codeowners": ["@garbled1", "@epenet"]
|
||||
}
|
||||
|
|
35
homeassistant/components/onewire/onewirehub.py
Normal file
35
homeassistant/components/onewire/onewirehub.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
"""Hub for communication with 1-Wire server or mount_dir."""
|
||||
import logging
|
||||
import os
|
||||
|
||||
from pyownet import protocol
|
||||
|
||||
from homeassistant.helpers.typing import HomeAssistantType
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OneWireHub:
|
||||
"""Hub to communicate with SysBus or OWServer."""
|
||||
|
||||
def __init__(self, hass: HomeAssistantType):
|
||||
"""Initialize."""
|
||||
self.hass = hass
|
||||
|
||||
async def can_connect(self, host, port) -> bool:
|
||||
"""Test if we can authenticate with the host."""
|
||||
try:
|
||||
await self.hass.async_add_executor_job(protocol.proxy, host, port)
|
||||
except (protocol.Error, protocol.ConnError) as exc:
|
||||
_LOGGER.error(
|
||||
"Cannot connect to owserver on %s:%d, got: %s", host, port, exc
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
async def is_valid_mount_dir(self, mount_dir) -> bool:
|
||||
"""Test that the mount_dir is a valid path."""
|
||||
if not await self.hass.async_add_executor_job(os.path.isdir, mount_dir):
|
||||
_LOGGER.error("Cannot find directory %s", mount_dir)
|
||||
return False
|
||||
return True
|
|
@ -8,9 +8,11 @@ from pyownet import protocol
|
|||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PORT,
|
||||
CONF_TYPE,
|
||||
ELECTRICAL_CURRENT_AMPERE,
|
||||
LIGHT_LUX,
|
||||
PERCENTAGE,
|
||||
|
@ -29,6 +31,7 @@ from .const import (
|
|||
CONF_TYPE_SYSBUS,
|
||||
DEFAULT_OWSERVER_PORT,
|
||||
DEFAULT_SYSBUS_MOUNT_DIR,
|
||||
DOMAIN,
|
||||
PRESSURE_CBAR,
|
||||
)
|
||||
|
||||
|
@ -125,36 +128,44 @@ def hb_info_from_type(dev_type="std"):
|
|||
return HOBBYBOARD_EF
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Old way of setting up 1-Wire platform."""
|
||||
if config.get(CONF_HOST):
|
||||
config[CONF_TYPE] = CONF_TYPE_OWSERVER
|
||||
elif config[CONF_MOUNT_DIR] == DEFAULT_SYSBUS_MOUNT_DIR:
|
||||
config[CONF_TYPE] = CONF_TYPE_SYSBUS
|
||||
else: # pragma: no cover
|
||||
# This part of the implementation does not conform to policy regarding 3rd-party libraries, and will not longer be updated.
|
||||
# https://developers.home-assistant.io/docs/creating_platform_code_review/#5-communication-with-devicesservices
|
||||
config[CONF_TYPE] = CONF_TYPE_OWFS
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=config
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up 1-Wire platform."""
|
||||
entities = get_entities(config)
|
||||
add_entities(entities, True)
|
||||
entities = await hass.async_add_executor_job(get_entities, config_entry.data)
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
def get_entities(config):
|
||||
"""Get a list of entities."""
|
||||
base_dir = config[CONF_MOUNT_DIR]
|
||||
owhost = config.get(CONF_HOST)
|
||||
owport = config[CONF_PORT]
|
||||
|
||||
# Ensure type is configured
|
||||
if owhost:
|
||||
conf_type = CONF_TYPE_OWSERVER
|
||||
elif base_dir == DEFAULT_SYSBUS_MOUNT_DIR:
|
||||
conf_type = CONF_TYPE_SYSBUS
|
||||
else: # pragma: no cover
|
||||
# This part of the implementation does not conform to policy regarding 3rd-party libraries, and will not longer be updated.
|
||||
# https://developers.home-assistant.io/docs/creating_platform_code_review/#5-communication-with-devicesservices
|
||||
conf_type = CONF_TYPE_OWFS
|
||||
|
||||
entities = []
|
||||
device_names = {}
|
||||
if CONF_NAMES in config:
|
||||
if isinstance(config[CONF_NAMES], dict):
|
||||
device_names = config[CONF_NAMES]
|
||||
|
||||
conf_type = config[CONF_TYPE]
|
||||
# We have an owserver on a remote(or local) host/port
|
||||
if conf_type == CONF_TYPE_OWSERVER:
|
||||
owhost = config[CONF_HOST]
|
||||
owport = config[CONF_PORT]
|
||||
|
||||
_LOGGER.debug("Initializing using %s:%s", owhost, owport)
|
||||
try:
|
||||
owproxy = protocol.proxy(host=owhost, port=owport)
|
||||
|
@ -163,7 +174,7 @@ def get_entities(config):
|
|||
_LOGGER.error(
|
||||
"Cannot connect to owserver on %s:%d, got: %s", owhost, owport, exc
|
||||
)
|
||||
devices = []
|
||||
return entities
|
||||
for device in devices:
|
||||
_LOGGER.debug("Found device: %s", device)
|
||||
family = owproxy.read(f"{device}family").decode()
|
||||
|
@ -200,8 +211,9 @@ def get_entities(config):
|
|||
|
||||
# We have a raw GPIO ow sensor on a Pi
|
||||
elif conf_type == CONF_TYPE_SYSBUS:
|
||||
_LOGGER.debug("Initializing using SysBus")
|
||||
for p1sensor in Pi1Wire().find_all_sensors():
|
||||
base_dir = config[CONF_MOUNT_DIR]
|
||||
_LOGGER.debug("Initializing using SysBus %s", base_dir)
|
||||
for p1sensor in Pi1Wire(base_dir).find_all_sensors():
|
||||
family = p1sensor.mac_address[:2]
|
||||
sensor_id = f"{family}-{p1sensor.mac_address[2:]}"
|
||||
if family not in DEVICE_SUPPORT_SYSBUS:
|
||||
|
@ -232,6 +244,7 @@ def get_entities(config):
|
|||
else: # pragma: no cover
|
||||
# This part of the implementation does not conform to policy regarding 3rd-party libraries, and will not longer be updated.
|
||||
# https://developers.home-assistant.io/docs/creating_platform_code_review/#5-communication-with-devicesservices
|
||||
base_dir = config[CONF_MOUNT_DIR]
|
||||
_LOGGER.debug("Initializing using OWFS %s", base_dir)
|
||||
_LOGGER.warning(
|
||||
"The OWFS implementation of 1-Wire sensors is deprecated, "
|
||||
|
|
26
homeassistant/components/onewire/strings.json
Normal file
26
homeassistant/components/onewire/strings.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_path": "Directory not found."
|
||||
},
|
||||
"step": {
|
||||
"owserver": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
},
|
||||
"title": "Set owserver details"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"type": "Connection type"
|
||||
},
|
||||
"title": "Set up 1-Wire"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
homeassistant/components/onewire/translations/en.json
Normal file
26
homeassistant/components/onewire/translations/en.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Service is already configured"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Unable to connect.",
|
||||
"invalid_path": "Directory not found."
|
||||
},
|
||||
"step": {
|
||||
"owserver": {
|
||||
"data": {
|
||||
"host": "Host",
|
||||
"port": "Port"
|
||||
},
|
||||
"title": "Set owserver details"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"type": "Connection type"
|
||||
},
|
||||
"title": "Set up 1-Wire"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -132,6 +132,7 @@ FLOWS = [
|
|||
"nws",
|
||||
"nzbget",
|
||||
"omnilogic",
|
||||
"onewire",
|
||||
"onvif",
|
||||
"opentherm_gw",
|
||||
"openuv",
|
||||
|
|
|
@ -1 +1,60 @@
|
|||
"""Tests for 1-Wire integration."""
|
||||
from asynctest.mock import patch
|
||||
|
||||
from homeassistant.components.onewire.const import (
|
||||
CONF_TYPE_OWSERVER,
|
||||
CONF_TYPE_SYSBUS,
|
||||
DEFAULT_SYSBUS_MOUNT_DIR,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import CONN_CLASS_LOCAL_POLL
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def setup_onewire_sysbus_integration(hass):
|
||||
"""Create the 1-Wire integration."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
source="user",
|
||||
data={
|
||||
CONF_TYPE: CONF_TYPE_SYSBUS,
|
||||
},
|
||||
unique_id=f"{CONF_TYPE_SYSBUS}:{DEFAULT_SYSBUS_MOUNT_DIR}",
|
||||
connection_class=CONN_CLASS_LOCAL_POLL,
|
||||
options={},
|
||||
entry_id="1",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return config_entry
|
||||
|
||||
|
||||
async def setup_onewire_owserver_integration(hass):
|
||||
"""Create the 1-Wire integration."""
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
source="user",
|
||||
data={
|
||||
CONF_TYPE: CONF_TYPE_OWSERVER,
|
||||
CONF_HOST: "1.2.3.4",
|
||||
CONF_PORT: "1234",
|
||||
},
|
||||
unique_id=f"{CONF_TYPE_OWSERVER}:1.2.3.4:1234",
|
||||
connection_class=CONN_CLASS_LOCAL_POLL,
|
||||
options={},
|
||||
entry_id="2",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onewire.sensor.protocol.proxy",
|
||||
):
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return config_entry
|
||||
|
|
333
tests/components/onewire/test_config_flow.py
Normal file
333
tests/components/onewire/test_config_flow.py
Normal file
|
@ -0,0 +1,333 @@
|
|||
"""Tests for 1-Wire config flow."""
|
||||
from pyownet import protocol
|
||||
|
||||
from homeassistant.components.onewire.const import (
|
||||
CONF_MOUNT_DIR,
|
||||
CONF_TYPE_OWSERVER,
|
||||
CONF_TYPE_SYSBUS,
|
||||
DEFAULT_OWSERVER_PORT,
|
||||
DEFAULT_SYSBUS_MOUNT_DIR,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
|
||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TYPE
|
||||
from homeassistant.data_entry_flow import (
|
||||
RESULT_TYPE_ABORT,
|
||||
RESULT_TYPE_CREATE_ENTRY,
|
||||
RESULT_TYPE_FORM,
|
||||
)
|
||||
|
||||
from . import setup_onewire_owserver_integration, setup_onewire_sysbus_integration
|
||||
|
||||
from tests.async_mock import patch
|
||||
|
||||
|
||||
async def test_user_owserver(hass):
|
||||
"""Test OWServer user flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert not result["errors"]
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TYPE: CONF_TYPE_OWSERVER},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "owserver"
|
||||
assert not result["errors"]
|
||||
|
||||
# Invalid server
|
||||
with patch(
|
||||
"homeassistant.components.onewire.onewirehub.protocol.proxy",
|
||||
side_effect=protocol.ConnError,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_HOST: "1.2.3.4", CONF_PORT: 1234},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "owserver"
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
# Valid server
|
||||
with patch("homeassistant.components.onewire.onewirehub.protocol.proxy",), patch(
|
||||
"homeassistant.components.onewire.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.onewire.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_HOST: "1.2.3.4", CONF_PORT: 1234},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "1.2.3.4"
|
||||
assert result["data"] == {
|
||||
CONF_TYPE: CONF_TYPE_OWSERVER,
|
||||
CONF_HOST: "1.2.3.4",
|
||||
CONF_PORT: 1234,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_user_owserver_duplicate(hass):
|
||||
"""Test OWServer flow."""
|
||||
with patch(
|
||||
"homeassistant.components.onewire.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.onewire.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
await setup_onewire_owserver_integration(hass)
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert not result["errors"]
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TYPE: CONF_TYPE_OWSERVER},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "owserver"
|
||||
assert not result["errors"]
|
||||
|
||||
# Duplicate server
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_HOST: "1.2.3.4", CONF_PORT: 1234},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_user_sysbus(hass):
|
||||
"""Test SysBus flow."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert not result["errors"]
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TYPE: CONF_TYPE_SYSBUS},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "mount_dir"
|
||||
assert not result["errors"]
|
||||
|
||||
# Invalid path
|
||||
with patch(
|
||||
"homeassistant.components.onewire.onewirehub.os.path.isdir",
|
||||
return_value=False,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_MOUNT_DIR: "/sys/bus/invalid_directory"},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "mount_dir"
|
||||
assert result["errors"] == {"base": "invalid_path"}
|
||||
|
||||
# Valid path
|
||||
with patch(
|
||||
"homeassistant.components.onewire.onewirehub.os.path.isdir",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.onewire.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.onewire.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_MOUNT_DIR: "/sys/bus/directory"},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "/sys/bus/directory"
|
||||
assert result["data"] == {
|
||||
CONF_TYPE: CONF_TYPE_SYSBUS,
|
||||
CONF_MOUNT_DIR: "/sys/bus/directory",
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_user_sysbus_duplicate(hass):
|
||||
"""Test SysBus duplicate flow."""
|
||||
with patch(
|
||||
"homeassistant.components.onewire.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.onewire.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
await setup_onewire_sysbus_integration(hass)
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert not result["errors"]
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_TYPE: CONF_TYPE_SYSBUS},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_FORM
|
||||
assert result["step_id"] == "mount_dir"
|
||||
assert not result["errors"]
|
||||
|
||||
# Valid path
|
||||
with patch(
|
||||
"homeassistant.components.onewire.onewirehub.os.path.isdir",
|
||||
return_value=True,
|
||||
):
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_MOUNT_DIR: DEFAULT_SYSBUS_MOUNT_DIR},
|
||||
)
|
||||
|
||||
assert result["type"] == RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_import_sysbus(hass):
|
||||
"""Test import step."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onewire.onewirehub.os.path.isdir",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.onewire.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.onewire.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={CONF_TYPE: CONF_TYPE_SYSBUS},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == DEFAULT_SYSBUS_MOUNT_DIR
|
||||
assert result["data"] == {
|
||||
CONF_TYPE: CONF_TYPE_SYSBUS,
|
||||
CONF_MOUNT_DIR: DEFAULT_SYSBUS_MOUNT_DIR,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_import_sysbus_with_mount_dir(hass):
|
||||
"""Test import step."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onewire.onewirehub.os.path.isdir",
|
||||
return_value=True,
|
||||
), patch(
|
||||
"homeassistant.components.onewire.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.onewire.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_TYPE: CONF_TYPE_SYSBUS,
|
||||
CONF_MOUNT_DIR: DEFAULT_SYSBUS_MOUNT_DIR,
|
||||
},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == DEFAULT_SYSBUS_MOUNT_DIR
|
||||
assert result["data"] == {
|
||||
CONF_TYPE: CONF_TYPE_SYSBUS,
|
||||
CONF_MOUNT_DIR: DEFAULT_SYSBUS_MOUNT_DIR,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_import_owserver(hass):
|
||||
"""Test import step."""
|
||||
|
||||
with patch("homeassistant.components.onewire.onewirehub.protocol.proxy",), patch(
|
||||
"homeassistant.components.onewire.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.onewire.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_TYPE: CONF_TYPE_OWSERVER,
|
||||
CONF_HOST: "1.2.3.4",
|
||||
},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "1.2.3.4"
|
||||
assert result["data"] == {
|
||||
CONF_TYPE: CONF_TYPE_OWSERVER,
|
||||
CONF_HOST: "1.2.3.4",
|
||||
CONF_PORT: DEFAULT_OWSERVER_PORT,
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_import_owserver_with_port(hass):
|
||||
"""Test import step."""
|
||||
|
||||
with patch("homeassistant.components.onewire.onewirehub.protocol.proxy",), patch(
|
||||
"homeassistant.components.onewire.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.onewire.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_TYPE: CONF_TYPE_OWSERVER,
|
||||
CONF_HOST: "1.2.3.4",
|
||||
CONF_PORT: "1234",
|
||||
},
|
||||
)
|
||||
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == "1.2.3.4"
|
||||
assert result["data"] == {
|
||||
CONF_TYPE: CONF_TYPE_OWSERVER,
|
||||
CONF_HOST: "1.2.3.4",
|
||||
CONF_PORT: "1234",
|
||||
}
|
||||
await hass.async_block_till_done()
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
|
@ -1,5 +1,5 @@
|
|||
"""Tests for 1-Wire devices connected on SysBus."""
|
||||
from unittest.mock import PropertyMock, patch
|
||||
from unittest.mock import patch
|
||||
|
||||
from pi1wire import InvalidCRCException, UnsupportResponseException
|
||||
import pytest
|
||||
|
@ -93,19 +93,18 @@ async def test_onewiredirect_setup_valid_device(hass, device_id):
|
|||
"""Test that sysbus config entry works correctly."""
|
||||
entity_registry = mock_registry(hass)
|
||||
|
||||
glob_result = [f"/{DEFAULT_SYSBUS_MOUNT_DIR}/{device_id}"]
|
||||
read_side_effect = []
|
||||
expected_sensors = MOCK_DEVICE_SENSORS[device_id]["sensors"]
|
||||
for expected_sensor in expected_sensors:
|
||||
read_side_effect.append(expected_sensor["injected_value"])
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.onewire.sensor.Pi1Wire"
|
||||
) as mock_pi1wire, patch("pi1wire.OneWire") as mock_owsensor:
|
||||
type(mock_owsensor).mac_address = PropertyMock(
|
||||
return_value=device_id.replace("-", "")
|
||||
)
|
||||
mock_owsensor.get_temperature.side_effect = read_side_effect
|
||||
mock_pi1wire.return_value.find_all_sensors.return_value = [mock_owsensor]
|
||||
"homeassistant.components.onewire.sensor.os.path.isdir", return_value=True
|
||||
), patch("pi1wire._finder.glob.glob", return_value=glob_result,), patch(
|
||||
"pi1wire.OneWire.get_temperature",
|
||||
side_effect=read_side_effect,
|
||||
):
|
||||
assert await async_setup_component(hass, SENSOR_DOMAIN, MOCK_CONFIG)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
|
23
tests/components/onewire/test_init.py
Normal file
23
tests/components/onewire/test_init.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
"""Tests for 1-Wire config flow."""
|
||||
from homeassistant.components.onewire.const import DOMAIN
|
||||
from homeassistant.config_entries import ENTRY_STATE_LOADED, ENTRY_STATE_NOT_LOADED
|
||||
|
||||
from . import setup_onewire_owserver_integration, setup_onewire_sysbus_integration
|
||||
|
||||
|
||||
async def test_unload_entry(hass):
|
||||
"""Test being able to unload an entry."""
|
||||
config_entry_owserver = await setup_onewire_owserver_integration(hass)
|
||||
config_entry_sysbus = await setup_onewire_sysbus_integration(hass)
|
||||
|
||||
assert len(hass.config_entries.async_entries(DOMAIN)) == 2
|
||||
assert config_entry_owserver.state == ENTRY_STATE_LOADED
|
||||
assert config_entry_sysbus.state == ENTRY_STATE_LOADED
|
||||
|
||||
assert await hass.config_entries.async_unload(config_entry_owserver.entry_id)
|
||||
assert await hass.config_entries.async_unload(config_entry_sysbus.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry_owserver.state == ENTRY_STATE_NOT_LOADED
|
||||
assert config_entry_sysbus.state == ENTRY_STATE_NOT_LOADED
|
||||
assert not hass.data.get(DOMAIN)
|
|
@ -7,7 +7,7 @@ from tests.common import assert_setup_component
|
|||
|
||||
|
||||
async def test_setup_minimum(hass):
|
||||
"""Test setup with minimum configuration."""
|
||||
"""Test old platform setup with minimum configuration."""
|
||||
config = {"sensor": {"platform": "onewire"}}
|
||||
with assert_setup_component(1, "sensor"):
|
||||
assert await async_setup_component(hass, sensor.DOMAIN, config)
|
||||
|
@ -15,7 +15,7 @@ async def test_setup_minimum(hass):
|
|||
|
||||
|
||||
async def test_setup_sysbus(hass):
|
||||
"""Test setup with SysBus configuration."""
|
||||
"""Test old platform setup with SysBus configuration."""
|
||||
config = {
|
||||
"sensor": {
|
||||
"platform": "onewire",
|
||||
|
@ -28,7 +28,7 @@ async def test_setup_sysbus(hass):
|
|||
|
||||
|
||||
async def test_setup_owserver(hass):
|
||||
"""Test setup with OWServer configuration."""
|
||||
"""Test old platform setup with OWServer configuration."""
|
||||
config = {"sensor": {"platform": "onewire", "host": "localhost"}}
|
||||
with assert_setup_component(1, "sensor"):
|
||||
assert await async_setup_component(hass, sensor.DOMAIN, config)
|
||||
|
@ -36,7 +36,7 @@ async def test_setup_owserver(hass):
|
|||
|
||||
|
||||
async def test_setup_owserver_with_port(hass):
|
||||
"""Test setup with OWServer configuration."""
|
||||
"""Test old platform setup with OWServer configuration."""
|
||||
config = {"sensor": {"platform": "onewire", "host": "localhost", "port": "1234"}}
|
||||
with assert_setup_component(1, "sensor"):
|
||||
assert await async_setup_component(hass, sensor.DOMAIN, config)
|
||||
|
|
Loading…
Add table
Reference in a new issue