249 lines
7.7 KiB
Python
249 lines
7.7 KiB
Python
"""Test the DSMR config flow."""
|
|
import asyncio
|
|
from itertools import chain, repeat
|
|
|
|
from dsmr_parser.clients.protocol import DSMRProtocol
|
|
from dsmr_parser.obis_references import EQUIPMENT_IDENTIFIER, EQUIPMENT_IDENTIFIER_GAS
|
|
from dsmr_parser.objects import CosemObject
|
|
import pytest
|
|
import serial
|
|
|
|
from homeassistant import config_entries, setup
|
|
from homeassistant.components.dsmr import DOMAIN
|
|
|
|
from tests.async_mock import DEFAULT, AsyncMock, Mock, patch
|
|
from tests.common import MockConfigEntry
|
|
|
|
SERIAL_DATA = {"serial_id": "12345678", "serial_id_gas": "123456789"}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_connection_factory(monkeypatch):
|
|
"""Mock the create functions for serial and TCP Asyncio connections."""
|
|
transport = Mock(spec=asyncio.Transport)
|
|
protocol = Mock(spec=DSMRProtocol)
|
|
|
|
async def connection_factory(*args, **kwargs):
|
|
"""Return mocked out Asyncio classes."""
|
|
return (transport, protocol)
|
|
|
|
connection_factory = Mock(wraps=connection_factory)
|
|
|
|
# apply the mock to both connection factories
|
|
monkeypatch.setattr(
|
|
"homeassistant.components.dsmr.config_flow.create_dsmr_reader",
|
|
connection_factory,
|
|
)
|
|
monkeypatch.setattr(
|
|
"homeassistant.components.dsmr.config_flow.create_tcp_dsmr_reader",
|
|
connection_factory,
|
|
)
|
|
|
|
protocol.telegram = {
|
|
EQUIPMENT_IDENTIFIER: CosemObject([{"value": "12345678", "unit": ""}]),
|
|
EQUIPMENT_IDENTIFIER_GAS: CosemObject([{"value": "123456789", "unit": ""}]),
|
|
}
|
|
|
|
async def wait_closed():
|
|
if isinstance(connection_factory.call_args_list[0][0][2], str):
|
|
# TCP
|
|
telegram_callback = connection_factory.call_args_list[0][0][3]
|
|
else:
|
|
# Serial
|
|
telegram_callback = connection_factory.call_args_list[0][0][2]
|
|
|
|
telegram_callback(protocol.telegram)
|
|
|
|
protocol.wait_closed = wait_closed
|
|
|
|
return connection_factory, transport, protocol
|
|
|
|
|
|
async def test_import_usb(hass, mock_connection_factory):
|
|
"""Test we can import."""
|
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
|
|
|
entry_data = {
|
|
"port": "/dev/ttyUSB0",
|
|
"dsmr_version": "2.2",
|
|
"precision": 4,
|
|
"reconnect_interval": 30,
|
|
}
|
|
|
|
with patch("homeassistant.components.dsmr.async_setup_entry", return_value=True):
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": config_entries.SOURCE_IMPORT},
|
|
data=entry_data,
|
|
)
|
|
|
|
assert result["type"] == "create_entry"
|
|
assert result["title"] == "/dev/ttyUSB0"
|
|
assert result["data"] == {**entry_data, **SERIAL_DATA}
|
|
|
|
|
|
async def test_import_usb_failed_connection(hass, monkeypatch, mock_connection_factory):
|
|
"""Test we can import."""
|
|
(connection_factory, transport, protocol) = mock_connection_factory
|
|
|
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
|
|
|
entry_data = {
|
|
"port": "/dev/ttyUSB0",
|
|
"dsmr_version": "2.2",
|
|
"precision": 4,
|
|
"reconnect_interval": 30,
|
|
}
|
|
|
|
# override the mock to have it fail the first time and succeed after
|
|
first_fail_connection_factory = AsyncMock(
|
|
return_value=(transport, protocol),
|
|
side_effect=chain([serial.serialutil.SerialException], repeat(DEFAULT)),
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
"homeassistant.components.dsmr.config_flow.create_dsmr_reader",
|
|
first_fail_connection_factory,
|
|
)
|
|
|
|
with patch("homeassistant.components.dsmr.async_setup_entry", return_value=True):
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": config_entries.SOURCE_IMPORT},
|
|
data=entry_data,
|
|
)
|
|
|
|
assert result["type"] == "abort"
|
|
assert result["reason"] == "cannot_connect"
|
|
|
|
|
|
async def test_import_usb_no_data(hass, monkeypatch, mock_connection_factory):
|
|
"""Test we can import."""
|
|
(connection_factory, transport, protocol) = mock_connection_factory
|
|
|
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
|
|
|
entry_data = {
|
|
"port": "/dev/ttyUSB0",
|
|
"dsmr_version": "2.2",
|
|
"precision": 4,
|
|
"reconnect_interval": 30,
|
|
}
|
|
|
|
# override the mock to have it fail the first time and succeed after
|
|
wait_closed = AsyncMock(
|
|
return_value=(transport, protocol),
|
|
side_effect=chain([asyncio.TimeoutError], repeat(DEFAULT)),
|
|
)
|
|
|
|
protocol.wait_closed = wait_closed
|
|
|
|
with patch("homeassistant.components.dsmr.async_setup_entry", return_value=True):
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": config_entries.SOURCE_IMPORT},
|
|
data=entry_data,
|
|
)
|
|
|
|
assert result["type"] == "abort"
|
|
assert result["reason"] == "cannot_communicate"
|
|
|
|
|
|
async def test_import_usb_wrong_telegram(hass, mock_connection_factory):
|
|
"""Test we can import."""
|
|
(connection_factory, transport, protocol) = mock_connection_factory
|
|
|
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
|
|
|
entry_data = {
|
|
"port": "/dev/ttyUSB0",
|
|
"dsmr_version": "2.2",
|
|
"precision": 4,
|
|
"reconnect_interval": 30,
|
|
}
|
|
|
|
protocol.telegram = {}
|
|
|
|
with patch("homeassistant.components.dsmr.async_setup_entry", return_value=True):
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": config_entries.SOURCE_IMPORT},
|
|
data=entry_data,
|
|
)
|
|
|
|
assert result["type"] == "abort"
|
|
assert result["reason"] == "cannot_communicate"
|
|
|
|
|
|
async def test_import_network(hass, mock_connection_factory):
|
|
"""Test we can import from network."""
|
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
|
|
|
entry_data = {
|
|
"host": "localhost",
|
|
"port": "1234",
|
|
"dsmr_version": "2.2",
|
|
"precision": 4,
|
|
"reconnect_interval": 30,
|
|
}
|
|
|
|
with patch("homeassistant.components.dsmr.async_setup_entry", return_value=True):
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": config_entries.SOURCE_IMPORT},
|
|
data=entry_data,
|
|
)
|
|
|
|
assert result["type"] == "create_entry"
|
|
assert result["title"] == "localhost:1234"
|
|
assert result["data"] == {**entry_data, **SERIAL_DATA}
|
|
|
|
|
|
async def test_import_update(hass, mock_connection_factory):
|
|
"""Test we can import."""
|
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
|
|
|
entry_data = {
|
|
"port": "/dev/ttyUSB0",
|
|
"dsmr_version": "2.2",
|
|
"precision": 4,
|
|
"reconnect_interval": 30,
|
|
}
|
|
|
|
entry = MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data=entry_data,
|
|
unique_id="/dev/ttyUSB0",
|
|
)
|
|
entry.add_to_hass(hass)
|
|
|
|
with patch(
|
|
"homeassistant.components.dsmr.async_setup_entry", return_value=True
|
|
), patch("homeassistant.components.dsmr.async_unload_entry", return_value=True):
|
|
await hass.config_entries.async_setup(entry.entry_id)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
new_entry_data = {
|
|
"port": "/dev/ttyUSB0",
|
|
"dsmr_version": "2.2",
|
|
"precision": 3,
|
|
"reconnect_interval": 30,
|
|
}
|
|
|
|
with patch(
|
|
"homeassistant.components.dsmr.async_setup_entry", return_value=True
|
|
), patch("homeassistant.components.dsmr.async_unload_entry", return_value=True):
|
|
result = await hass.config_entries.flow.async_init(
|
|
DOMAIN,
|
|
context={"source": config_entries.SOURCE_IMPORT},
|
|
data=new_entry_data,
|
|
)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert result["type"] == "abort"
|
|
assert result["reason"] == "already_configured"
|
|
|
|
assert entry.data["precision"] == 3
|