1
mirror of https://github.com/home-assistant/core synced 2024-09-15 17:29:45 +02:00

Remove mysensors yaml (#72761)

This commit is contained in:
Martin Hjelmare 2022-05-31 15:22:31 +02:00 committed by GitHub
parent cf27b82d2f
commit 8140ed724c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 87 additions and 651 deletions

View File

@ -7,12 +7,9 @@ from functools import partial
import logging import logging
from mysensors import BaseAsyncGateway from mysensors import BaseAsyncGateway
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.components.mqtt import valid_publish_topic, valid_subscribe_topic
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_OPTIMISTIC, Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.device_registry import DeviceEntry
@ -22,17 +19,6 @@ from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import (
ATTR_DEVICES, ATTR_DEVICES,
CONF_BAUD_RATE,
CONF_DEVICE,
CONF_GATEWAYS,
CONF_NODES,
CONF_PERSISTENCE,
CONF_PERSISTENCE_FILE,
CONF_RETAIN,
CONF_TCP_PORT,
CONF_TOPIC_IN_PREFIX,
CONF_TOPIC_OUT_PREFIX,
CONF_VERSION,
DOMAIN, DOMAIN,
MYSENSORS_DISCOVERY, MYSENSORS_DISCOVERY,
MYSENSORS_GATEWAYS, MYSENSORS_GATEWAYS,
@ -48,147 +34,17 @@ from .helpers import on_unload
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
CONF_DEBUG = "debug"
CONF_NODE_NAME = "name"
DATA_HASS_CONFIG = "hass_config" DATA_HASS_CONFIG = "hass_config"
DEFAULT_BAUD_RATE = 115200
DEFAULT_TCP_PORT = 5003
DEFAULT_VERSION = "1.4"
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
def set_default_persistence_file(value: dict) -> dict:
"""Set default persistence file."""
for idx, gateway in enumerate(value):
if gateway.get(CONF_PERSISTENCE_FILE) is not None:
continue
new_name = f"mysensors{idx + 1}.pickle"
gateway[CONF_PERSISTENCE_FILE] = new_name
return value
def has_all_unique_files(value: list[dict]) -> list[dict]:
"""Validate that all persistence files are unique and set if any is set."""
persistence_files = [gateway[CONF_PERSISTENCE_FILE] for gateway in value]
schema = vol.Schema(vol.Unique())
schema(persistence_files)
return value
def is_persistence_file(value: str) -> str:
"""Validate that persistence file path ends in either .pickle or .json."""
if value.endswith((".json", ".pickle")):
return value
raise vol.Invalid(f"{value} does not end in either `.json` or `.pickle`")
def deprecated(key: str) -> Callable[[dict], dict]:
"""Mark key as deprecated in configuration."""
def validator(config: dict) -> dict:
"""Check if key is in config, log warning and remove key."""
if key not in config:
return config
_LOGGER.warning(
"%s option for %s is deprecated. Please remove %s from your "
"configuration file",
key,
DOMAIN,
key,
)
config.pop(key)
return config
return validator
NODE_SCHEMA = vol.Schema({cv.positive_int: {vol.Required(CONF_NODE_NAME): cv.string}})
GATEWAY_SCHEMA = vol.Schema(
vol.All(
deprecated(CONF_NODES),
{
vol.Required(CONF_DEVICE): cv.string,
vol.Optional(CONF_PERSISTENCE_FILE): vol.All(
cv.string, is_persistence_file
),
vol.Optional(CONF_BAUD_RATE, default=DEFAULT_BAUD_RATE): cv.positive_int,
vol.Optional(CONF_TCP_PORT, default=DEFAULT_TCP_PORT): cv.port,
vol.Optional(CONF_TOPIC_IN_PREFIX): valid_subscribe_topic,
vol.Optional(CONF_TOPIC_OUT_PREFIX): valid_publish_topic,
vol.Optional(CONF_NODES, default={}): NODE_SCHEMA,
},
)
)
CONFIG_SCHEMA = vol.Schema(
vol.All(
cv.deprecated(DOMAIN),
{
DOMAIN: vol.Schema(
vol.All(
deprecated(CONF_DEBUG),
deprecated(CONF_OPTIMISTIC),
deprecated(CONF_PERSISTENCE),
{
vol.Required(CONF_GATEWAYS): vol.All(
cv.ensure_list,
set_default_persistence_file,
has_all_unique_files,
[GATEWAY_SCHEMA],
),
vol.Optional(CONF_RETAIN, default=True): cv.boolean,
vol.Optional(CONF_VERSION, default=DEFAULT_VERSION): cv.string,
vol.Optional(CONF_OPTIMISTIC, default=False): cv.boolean,
vol.Optional(CONF_PERSISTENCE, default=True): cv.boolean,
},
)
)
},
),
extra=vol.ALLOW_EXTRA,
)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the MySensors component.""" """Set up the MySensors component."""
# This is needed to set up the notify platform via discovery.
hass.data[DOMAIN] = {DATA_HASS_CONFIG: config} hass.data[DOMAIN] = {DATA_HASS_CONFIG: config}
if DOMAIN not in config or bool(hass.config_entries.async_entries(DOMAIN)):
return True
config = config[DOMAIN]
user_inputs = [
{
CONF_DEVICE: gw[CONF_DEVICE],
CONF_BAUD_RATE: gw[CONF_BAUD_RATE],
CONF_TCP_PORT: gw[CONF_TCP_PORT],
CONF_TOPIC_OUT_PREFIX: gw.get(CONF_TOPIC_OUT_PREFIX, ""),
CONF_TOPIC_IN_PREFIX: gw.get(CONF_TOPIC_IN_PREFIX, ""),
CONF_RETAIN: config[CONF_RETAIN],
CONF_VERSION: config[CONF_VERSION],
CONF_PERSISTENCE_FILE: gw[CONF_PERSISTENCE_FILE]
# nodes config ignored at this time. renaming nodes can now be done from the frontend.
}
for gw in config[CONF_GATEWAYS]
]
user_inputs = [
{k: v for k, v in userinput.items() if v is not None}
for userinput in user_inputs
]
# there is an actual configuration in configuration.yaml, so we have to process it
for user_input in user_inputs:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_IMPORT},
data=user_input,
)
)
return True return True

View File

@ -22,7 +22,6 @@ from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from . import DEFAULT_BAUD_RATE, DEFAULT_TCP_PORT, DEFAULT_VERSION, is_persistence_file
from .const import ( from .const import (
CONF_BAUD_RATE, CONF_BAUD_RATE,
CONF_DEVICE, CONF_DEVICE,
@ -42,6 +41,17 @@ from .const import (
) )
from .gateway import MQTT_COMPONENT, is_serial_port, is_socket_address, try_connect from .gateway import MQTT_COMPONENT, is_serial_port, is_socket_address, try_connect
DEFAULT_BAUD_RATE = 115200
DEFAULT_TCP_PORT = 5003
DEFAULT_VERSION = "1.4"
def is_persistence_file(value: str) -> str:
"""Validate that persistence file path ends in either .pickle or .json."""
if value.endswith((".json", ".pickle")):
return value
raise vol.Invalid(f"{value} does not end in either `.json` or `.pickle`")
def _get_schema_common(user_input: dict[str, str]) -> dict: def _get_schema_common(user_input: dict[str, str]) -> dict:
"""Create a schema with options common to all gateway types.""" """Create a schema with options common to all gateway types."""
@ -105,31 +115,6 @@ class MySensorsConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Set up config flow.""" """Set up config flow."""
self._gw_type: str | None = None self._gw_type: str | None = None
async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult:
"""Import a config entry.
This method is called by async_setup and it has already
prepared the dict to be compatible with what a user would have
entered from the frontend.
Therefore we process it as though it came from the frontend.
"""
if user_input[CONF_DEVICE] == MQTT_COMPONENT:
user_input[CONF_GATEWAY_TYPE] = CONF_GATEWAY_TYPE_MQTT
else:
try:
await self.hass.async_add_executor_job(
is_serial_port, user_input[CONF_DEVICE]
)
except vol.Invalid:
user_input[CONF_GATEWAY_TYPE] = CONF_GATEWAY_TYPE_TCP
else:
user_input[CONF_GATEWAY_TYPE] = CONF_GATEWAY_TYPE_SERIAL
result: FlowResult = await self.async_step_user(user_input=user_input)
if errors := result.get("errors"):
return self.async_abort(reason=next(iter(errors.values())))
return result
async def async_step_user( async def async_step_user(
self, user_input: dict[str, str] | None = None self, user_input: dict[str, str] | None = None
) -> FlowResult: ) -> FlowResult:
@ -335,10 +320,11 @@ class MySensorsConfigFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
errors[CONF_PERSISTENCE_FILE] = "duplicate_persistence_file" errors[CONF_PERSISTENCE_FILE] = "duplicate_persistence_file"
break break
for other_entry in self._async_current_entries(): if not errors:
if _is_same_device(gw_type, user_input, other_entry): for other_entry in self._async_current_entries():
errors["base"] = "already_configured" if _is_same_device(gw_type, user_input, other_entry):
break errors["base"] = "already_configured"
break
# if no errors so far, try to connect # if no errors so far, try to connect
if not errors and not await try_connect(self.hass, gw_type, user_input): if not errors and not await try_connect(self.hass, gw_type, user_input):

View File

@ -11,9 +11,6 @@ ATTR_GATEWAY_ID: Final = "gateway_id"
CONF_BAUD_RATE: Final = "baud_rate" CONF_BAUD_RATE: Final = "baud_rate"
CONF_DEVICE: Final = "device" CONF_DEVICE: Final = "device"
CONF_GATEWAYS: Final = "gateways"
CONF_NODES: Final = "nodes"
CONF_PERSISTENCE: Final = "persistence"
CONF_PERSISTENCE_FILE: Final = "persistence_file" CONF_PERSISTENCE_FILE: Final = "persistence_file"
CONF_RETAIN: Final = "retain" CONF_RETAIN: Final = "retain"
CONF_TCP_PORT: Final = "tcp_port" CONF_TCP_PORT: Final = "tcp_port"

View File

@ -13,13 +13,13 @@ import pytest
from homeassistant.components.device_tracker.legacy import Device from homeassistant.components.device_tracker.legacy import Device
from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN from homeassistant.components.mqtt import DOMAIN as MQTT_DOMAIN
from homeassistant.components.mysensors import CONF_VERSION, DEFAULT_BAUD_RATE from homeassistant.components.mysensors.config_flow import DEFAULT_BAUD_RATE
from homeassistant.components.mysensors.const import ( from homeassistant.components.mysensors.const import (
CONF_BAUD_RATE, CONF_BAUD_RATE,
CONF_DEVICE, CONF_DEVICE,
CONF_GATEWAY_TYPE, CONF_GATEWAY_TYPE,
CONF_GATEWAY_TYPE_SERIAL, CONF_GATEWAY_TYPE_SERIAL,
CONF_GATEWAYS, CONF_VERSION,
DOMAIN, DOMAIN,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -141,8 +141,7 @@ async def integration_fixture(
hass: HomeAssistant, transport: MagicMock, config_entry: MockConfigEntry hass: HomeAssistant, transport: MagicMock, config_entry: MockConfigEntry
) -> AsyncGenerator[MockConfigEntry, None]: ) -> AsyncGenerator[MockConfigEntry, None]:
"""Set up the mysensors integration with a config entry.""" """Set up the mysensors integration with a config entry."""
device = config_entry.data[CONF_DEVICE] config: dict[str, Any] = {}
config: dict[str, Any] = {DOMAIN: {CONF_GATEWAYS: [{CONF_DEVICE: device}]}}
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
with patch("homeassistant.components.mysensors.device.UPDATE_DELAY", new=0): with patch("homeassistant.components.mysensors.device.UPDATE_DELAY", new=0):

View File

@ -14,7 +14,6 @@ from homeassistant.components.mysensors.const import (
CONF_GATEWAY_TYPE_MQTT, CONF_GATEWAY_TYPE_MQTT,
CONF_GATEWAY_TYPE_SERIAL, CONF_GATEWAY_TYPE_SERIAL,
CONF_GATEWAY_TYPE_TCP, CONF_GATEWAY_TYPE_TCP,
CONF_PERSISTENCE,
CONF_PERSISTENCE_FILE, CONF_PERSISTENCE_FILE,
CONF_RETAIN, CONF_RETAIN,
CONF_TCP_PORT, CONF_TCP_PORT,
@ -399,333 +398,268 @@ async def test_config_invalid(
assert len(mock_setup_entry.mock_calls) == 0 assert len(mock_setup_entry.mock_calls) == 0
@pytest.mark.parametrize(
"user_input",
[
{
CONF_DEVICE: "COM5",
CONF_BAUD_RATE: 57600,
CONF_TCP_PORT: 5003,
CONF_RETAIN: True,
CONF_VERSION: "2.3",
CONF_PERSISTENCE_FILE: "bla.json",
},
{
CONF_DEVICE: "COM5",
CONF_PERSISTENCE_FILE: "bla.json",
CONF_BAUD_RATE: 57600,
CONF_TCP_PORT: 5003,
CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: True,
},
{
CONF_DEVICE: "mqtt",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_TOPIC_IN_PREFIX: "intopic",
CONF_TOPIC_OUT_PREFIX: "outtopic",
CONF_VERSION: "2.4",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
},
{
CONF_DEVICE: "127.0.0.1",
CONF_PERSISTENCE_FILE: "blub.pickle",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 343,
CONF_VERSION: "2.4",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
},
],
)
async def test_import(hass: HomeAssistant, mqtt: None, user_input: dict) -> None:
"""Test importing a gateway."""
with patch("sys.platform", "win32"), patch(
"homeassistant.components.mysensors.config_flow.try_connect", return_value=True
), patch(
"homeassistant.components.mysensors.async_setup_entry",
return_value=True,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, data=user_input, context={"source": config_entries.SOURCE_IMPORT}
)
await hass.async_block_till_done()
assert result["type"] == "create_entry"
@pytest.mark.parametrize( @pytest.mark.parametrize(
"first_input, second_input, expected_result", "first_input, second_input, expected_result",
[ [
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_DEVICE: "mqtt", CONF_DEVICE: "mqtt",
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_IN_PREFIX: "same1",
CONF_TOPIC_OUT_PREFIX: "same2", CONF_TOPIC_OUT_PREFIX: "same2",
}, },
{ {
CONF_DEVICE: "mqtt", CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_IN_PREFIX: "same1",
CONF_TOPIC_OUT_PREFIX: "same2", CONF_TOPIC_OUT_PREFIX: "same2",
}, },
(CONF_TOPIC_IN_PREFIX, "duplicate_topic"), FlowResult(type="form", errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_DEVICE: "mqtt", CONF_DEVICE: "mqtt",
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "different1", CONF_TOPIC_IN_PREFIX: "different1",
CONF_TOPIC_OUT_PREFIX: "different2", CONF_TOPIC_OUT_PREFIX: "different2",
}, },
{ {
CONF_DEVICE: "mqtt", CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "different3", CONF_TOPIC_IN_PREFIX: "different3",
CONF_TOPIC_OUT_PREFIX: "different4", CONF_TOPIC_OUT_PREFIX: "different4",
}, },
None, FlowResult(type="create_entry"),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_DEVICE: "mqtt", CONF_DEVICE: "mqtt",
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_IN_PREFIX: "same1",
CONF_TOPIC_OUT_PREFIX: "different2", CONF_TOPIC_OUT_PREFIX: "different2",
}, },
{ {
CONF_DEVICE: "mqtt", CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_IN_PREFIX: "same1",
CONF_TOPIC_OUT_PREFIX: "different4", CONF_TOPIC_OUT_PREFIX: "different4",
}, },
(CONF_TOPIC_IN_PREFIX, "duplicate_topic"), FlowResult(type="form", errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_DEVICE: "mqtt", CONF_DEVICE: "mqtt",
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_IN_PREFIX: "same1",
CONF_TOPIC_OUT_PREFIX: "different2", CONF_TOPIC_OUT_PREFIX: "different2",
}, },
{ {
CONF_DEVICE: "mqtt", CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "different1", CONF_TOPIC_IN_PREFIX: "different1",
CONF_TOPIC_OUT_PREFIX: "same1", CONF_TOPIC_OUT_PREFIX: "same1",
}, },
(CONF_TOPIC_OUT_PREFIX, "duplicate_topic"), FlowResult(type="form", errors={CONF_TOPIC_OUT_PREFIX: "duplicate_topic"}),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_DEVICE: "mqtt", CONF_DEVICE: "mqtt",
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_IN_PREFIX: "same1",
CONF_TOPIC_OUT_PREFIX: "different2", CONF_TOPIC_OUT_PREFIX: "different2",
}, },
{ {
CONF_DEVICE: "mqtt", CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_TOPIC_IN_PREFIX: "same1", CONF_TOPIC_IN_PREFIX: "same1",
CONF_TOPIC_OUT_PREFIX: "different1", CONF_TOPIC_OUT_PREFIX: "different1",
}, },
(CONF_TOPIC_IN_PREFIX, "duplicate_topic"), FlowResult(type="form", errors={CONF_TOPIC_IN_PREFIX: "duplicate_topic"}),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "127.0.0.1", CONF_DEVICE: "127.0.0.1",
CONF_PERSISTENCE_FILE: "same.json", CONF_PERSISTENCE_FILE: "same.json",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.2", CONF_DEVICE: "192.168.1.2",
CONF_PERSISTENCE_FILE: "same.json", CONF_PERSISTENCE_FILE: "same.json",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
("persistence_file", "duplicate_persistence_file"), FlowResult(
type="form", errors={"persistence_file": "duplicate_persistence_file"}
),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "127.0.0.1", CONF_DEVICE: "127.0.0.1",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.2", CONF_DEVICE: "192.168.1.2",
CONF_PERSISTENCE_FILE: "same.json", CONF_PERSISTENCE_FILE: "same.json",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
None, FlowResult(type="create_entry"),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "127.0.0.1", CONF_DEVICE: "127.0.0.1",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.2", CONF_DEVICE: "192.168.1.2",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
None, FlowResult(type="create_entry"),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.2", CONF_DEVICE: "192.168.1.2",
CONF_PERSISTENCE_FILE: "different1.json", CONF_PERSISTENCE_FILE: "different1.json",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.2", CONF_DEVICE: "192.168.1.2",
CONF_PERSISTENCE_FILE: "different2.json", CONF_PERSISTENCE_FILE: "different2.json",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
("base", "already_configured"), FlowResult(type="form", errors={"base": "already_configured"}),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.2", CONF_DEVICE: "192.168.1.2",
CONF_PERSISTENCE_FILE: "different1.json", CONF_PERSISTENCE_FILE: "different1.json",
CONF_TCP_PORT: 343, CONF_TCP_PORT: 343,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.2", CONF_DEVICE: "192.168.1.2",
CONF_PERSISTENCE_FILE: "different2.json", CONF_PERSISTENCE_FILE: "different2.json",
CONF_TCP_PORT: 5003, CONF_TCP_PORT: 5003,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
None, FlowResult(type="create_entry"),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.2", CONF_DEVICE: "192.168.1.2",
CONF_TCP_PORT: 5003, CONF_TCP_PORT: 5003,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "192.168.1.3", CONF_DEVICE: "192.168.1.3",
CONF_TCP_PORT: 5003, CONF_TCP_PORT: 5003,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}, },
None, FlowResult(type="create_entry"),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM5", CONF_DEVICE: "COM5",
CONF_TCP_PORT: 5003,
CONF_RETAIN: True,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE_FILE: "different1.json", CONF_PERSISTENCE_FILE: "different1.json",
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM5", CONF_DEVICE: "COM5",
CONF_TCP_PORT: 5003,
CONF_RETAIN: True,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE_FILE: "different2.json", CONF_PERSISTENCE_FILE: "different2.json",
}, },
("base", "already_configured"), FlowResult(type="form", errors={"base": "already_configured"}),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM6", CONF_DEVICE: "COM6",
CONF_BAUD_RATE: 57600, CONF_BAUD_RATE: 57600,
CONF_RETAIN: True,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM5", CONF_DEVICE: "COM5",
CONF_TCP_PORT: 5003,
CONF_RETAIN: True,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
}, },
None, FlowResult(type="create_entry"),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM5", CONF_DEVICE: "COM5",
CONF_BAUD_RATE: 115200, CONF_BAUD_RATE: 115200,
CONF_RETAIN: True,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE_FILE: "different1.json", CONF_PERSISTENCE_FILE: "different1.json",
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM5", CONF_DEVICE: "COM5",
CONF_BAUD_RATE: 57600, CONF_BAUD_RATE: 57600,
CONF_RETAIN: True,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE_FILE: "different2.json", CONF_PERSISTENCE_FILE: "different2.json",
}, },
("base", "already_configured"), FlowResult(type="form", errors={"base": "already_configured"}),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM5", CONF_DEVICE: "COM5",
CONF_BAUD_RATE: 115200, CONF_BAUD_RATE: 115200,
CONF_RETAIN: True,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE_FILE: "same.json", CONF_PERSISTENCE_FILE: "same.json",
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM6", CONF_DEVICE: "COM6",
CONF_BAUD_RATE: 57600, CONF_BAUD_RATE: 57600,
CONF_RETAIN: True,
CONF_VERSION: "2.3", CONF_VERSION: "2.3",
CONF_PERSISTENCE_FILE: "same.json", CONF_PERSISTENCE_FILE: "same.json",
}, },
("persistence_file", "duplicate_persistence_file"), FlowResult(
type="form", errors={"persistence_file": "duplicate_persistence_file"}
),
), ),
( (
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_DEVICE: "mqtt", CONF_DEVICE: "mqtt",
CONF_PERSISTENCE_FILE: "bla.json", CONF_PERSISTENCE_FILE: "bla.json",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_VERSION: "1.4", CONF_VERSION: "1.4",
}, },
{ {
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM6", CONF_DEVICE: "COM6",
CONF_PERSISTENCE_FILE: "bla2.json", CONF_PERSISTENCE_FILE: "bla2.json",
CONF_BAUD_RATE: 115200, CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_VERSION: "1.4", CONF_VERSION: "1.4",
}, },
None, FlowResult(type="create_entry"),
), ),
], ],
) )
@ -734,7 +668,7 @@ async def test_duplicate(
mqtt: None, mqtt: None,
first_input: dict, first_input: dict,
second_input: dict, second_input: dict,
expected_result: tuple[str, str] | None, expected_result: FlowResult,
) -> None: ) -> None:
"""Test duplicate detection.""" """Test duplicate detection."""
@ -746,12 +680,17 @@ async def test_duplicate(
): ):
MockConfigEntry(domain=DOMAIN, data=first_input).add_to_hass(hass) MockConfigEntry(domain=DOMAIN, data=first_input).add_to_hass(hass)
second_gateway_type = second_input.pop(CONF_GATEWAY_TYPE)
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(
DOMAIN, data=second_input, context={"source": config_entries.SOURCE_IMPORT} DOMAIN,
data={CONF_GATEWAY_TYPE: second_gateway_type},
context={"source": config_entries.SOURCE_USER},
)
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
second_input,
) )
await hass.async_block_till_done() await hass.async_block_till_done()
if expected_result is None:
assert result["type"] == "create_entry" for key, val in expected_result.items():
else: assert result[key] == val # type: ignore[literal-required]
assert result["type"] == "abort"
assert result["reason"] == expected_result[1]

View File

@ -2,360 +2,19 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Awaitable, Callable from collections.abc import Awaitable, Callable
from typing import Any
from unittest.mock import patch
from aiohttp import ClientWebSocketResponse from aiohttp import ClientWebSocketResponse
from mysensors import BaseSyncGateway from mysensors import BaseSyncGateway
from mysensors.sensor import Sensor from mysensors.sensor import Sensor
import pytest
from homeassistant.components.mysensors import ( from homeassistant.components.mysensors import DOMAIN
CONF_BAUD_RATE,
CONF_DEVICE,
CONF_GATEWAYS,
CONF_PERSISTENCE,
CONF_PERSISTENCE_FILE,
CONF_RETAIN,
CONF_TCP_PORT,
CONF_VERSION,
DEFAULT_VERSION,
DOMAIN,
)
from homeassistant.components.mysensors.const import (
CONF_GATEWAY_TYPE,
CONF_GATEWAY_TYPE_MQTT,
CONF_GATEWAY_TYPE_SERIAL,
CONF_GATEWAY_TYPE_TCP,
CONF_TOPIC_IN_PREFIX,
CONF_TOPIC_OUT_PREFIX,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@pytest.mark.parametrize(
"config, expected_calls, expected_to_succeed, expected_config_entry_data",
[
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "COM5",
CONF_PERSISTENCE_FILE: "bla.json",
CONF_BAUD_RATE: 57600,
CONF_TCP_PORT: 5003,
}
],
CONF_VERSION: "2.3",
CONF_PERSISTENCE: False,
CONF_RETAIN: True,
}
},
1,
True,
[
{
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
CONF_DEVICE: "COM5",
CONF_PERSISTENCE_FILE: "bla.json",
CONF_BAUD_RATE: 57600,
CONF_VERSION: "2.3",
CONF_TCP_PORT: 5003,
CONF_TOPIC_IN_PREFIX: "",
CONF_TOPIC_OUT_PREFIX: "",
CONF_RETAIN: True,
}
],
),
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "127.0.0.1",
CONF_PERSISTENCE_FILE: "blub.pickle",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 343,
}
],
CONF_VERSION: "2.4",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}
},
1,
True,
[
{
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "127.0.0.1",
CONF_PERSISTENCE_FILE: "blub.pickle",
CONF_TCP_PORT: 343,
CONF_VERSION: "2.4",
CONF_BAUD_RATE: 115200,
CONF_TOPIC_IN_PREFIX: "",
CONF_TOPIC_OUT_PREFIX: "",
CONF_RETAIN: False,
}
],
),
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "127.0.0.1",
}
],
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}
},
1,
True,
[
{
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_TCP,
CONF_DEVICE: "127.0.0.1",
CONF_TCP_PORT: 5003,
CONF_VERSION: DEFAULT_VERSION,
CONF_BAUD_RATE: 115200,
CONF_TOPIC_IN_PREFIX: "",
CONF_TOPIC_OUT_PREFIX: "",
CONF_RETAIN: False,
CONF_PERSISTENCE_FILE: "mysensors1.pickle",
}
],
),
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "mqtt",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_TOPIC_IN_PREFIX: "intopic",
CONF_TOPIC_OUT_PREFIX: "outtopic",
}
],
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}
},
1,
True,
[
{
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
CONF_DEVICE: "mqtt",
CONF_VERSION: DEFAULT_VERSION,
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_TOPIC_OUT_PREFIX: "outtopic",
CONF_TOPIC_IN_PREFIX: "intopic",
CONF_RETAIN: False,
CONF_PERSISTENCE_FILE: "mysensors1.pickle",
}
],
),
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "mqtt",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
}
],
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}
},
0,
True,
[{}],
),
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "mqtt",
CONF_PERSISTENCE_FILE: "bla.json",
CONF_TOPIC_OUT_PREFIX: "out",
CONF_TOPIC_IN_PREFIX: "in",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
},
{
CONF_DEVICE: "COM6",
CONF_PERSISTENCE_FILE: "bla2.json",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
},
],
CONF_VERSION: "2.4",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}
},
2,
True,
[
{
CONF_DEVICE: "mqtt",
CONF_PERSISTENCE_FILE: "bla.json",
CONF_TOPIC_OUT_PREFIX: "out",
CONF_TOPIC_IN_PREFIX: "in",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_VERSION: "2.4",
CONF_RETAIN: False,
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_MQTT,
},
{
CONF_DEVICE: "COM6",
CONF_PERSISTENCE_FILE: "bla2.json",
CONF_TOPIC_OUT_PREFIX: "",
CONF_TOPIC_IN_PREFIX: "",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_VERSION: "2.4",
CONF_RETAIN: False,
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
},
],
),
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "mqtt",
CONF_PERSISTENCE_FILE: "bla.json",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
},
{
CONF_DEVICE: "COM6",
CONF_PERSISTENCE_FILE: "bla.json",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
},
],
CONF_VERSION: "2.4",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}
},
0,
False,
[{}],
),
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "COMx",
CONF_PERSISTENCE_FILE: "bla.json",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
},
],
CONF_VERSION: "2.4",
CONF_PERSISTENCE: False,
CONF_RETAIN: False,
}
},
0,
True,
[{}],
),
(
{
DOMAIN: {
CONF_GATEWAYS: [
{
CONF_DEVICE: "COM1",
},
{
CONF_DEVICE: "COM2",
},
],
}
},
2,
True,
[
{
CONF_DEVICE: "COM1",
CONF_PERSISTENCE_FILE: "mysensors1.pickle",
CONF_TOPIC_OUT_PREFIX: "",
CONF_TOPIC_IN_PREFIX: "",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_VERSION: "1.4",
CONF_RETAIN: True,
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
},
{
CONF_DEVICE: "COM2",
CONF_PERSISTENCE_FILE: "mysensors2.pickle",
CONF_TOPIC_OUT_PREFIX: "",
CONF_TOPIC_IN_PREFIX: "",
CONF_BAUD_RATE: 115200,
CONF_TCP_PORT: 5003,
CONF_VERSION: "1.4",
CONF_RETAIN: True,
CONF_GATEWAY_TYPE: CONF_GATEWAY_TYPE_SERIAL,
},
],
),
],
)
async def test_import(
hass: HomeAssistant,
mqtt: None,
config: ConfigType,
expected_calls: int,
expected_to_succeed: bool,
expected_config_entry_data: list[dict[str, Any]],
) -> None:
"""Test importing a gateway."""
with patch("sys.platform", "win32"), patch(
"homeassistant.components.mysensors.config_flow.try_connect", return_value=True
), patch(
"homeassistant.components.mysensors.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await async_setup_component(hass, DOMAIN, config)
assert result == expected_to_succeed
await hass.async_block_till_done()
assert len(mock_setup_entry.mock_calls) == expected_calls
for idx in range(expected_calls):
config_entry = mock_setup_entry.mock_calls[idx][1][1]
expected_persistence_file = expected_config_entry_data[idx].pop(
CONF_PERSISTENCE_FILE
)
expected_persistence_path = hass.config.path(expected_persistence_file)
config_entry_data = dict(config_entry.data)
persistence_path = config_entry_data.pop(CONF_PERSISTENCE_FILE)
assert persistence_path == expected_persistence_path
assert config_entry_data == expected_config_entry_data[idx]
async def test_remove_config_entry_device( async def test_remove_config_entry_device(
hass: HomeAssistant, hass: HomeAssistant,
gps_sensor: Sensor, gps_sensor: Sensor,