Abort ESPHome connection when both name and mac address do not match (#98787)

This commit is contained in:
J. Nick Koston
2023-08-22 16:02:23 -05:00
committed by GitHub
parent 99b5c4932f
commit 49d73441bf
3 changed files with 298 additions and 62 deletions

View File

@@ -1,7 +1,5 @@
"""ESPHome set up tests."""
from unittest.mock import AsyncMock
from aioesphomeapi import DeviceInfo
from homeassistant.components.esphome import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
@@ -10,29 +8,6 @@ from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def test_unique_id_updated_to_mac(
hass: HomeAssistant, mock_client, mock_zeroconf: None
) -> None:
"""Test we update config entry unique ID to MAC address."""
entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: "test.local", CONF_PORT: 6053, CONF_PASSWORD: ""},
unique_id="mock-config-name",
)
entry.add_to_hass(hass)
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(
mac_address="1122334455aa",
)
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.unique_id == "11:22:33:44:55:aa"
async def test_delete_entry(
hass: HomeAssistant, mock_client, mock_zeroconf: None
) -> None:

View File

@@ -1,11 +1,20 @@
"""Test ESPHome manager."""
from collections.abc import Awaitable, Callable
from unittest.mock import AsyncMock
from aioesphomeapi import APIClient, EntityInfo, EntityState, UserService
from aioesphomeapi import APIClient, DeviceInfo, EntityInfo, EntityState, UserService
import pytest
from homeassistant.components.esphome.const import DOMAIN, STABLE_BLE_VERSION_STR
from homeassistant import config_entries
from homeassistant.components import dhcp
from homeassistant.components.esphome.const import (
CONF_DEVICE_NAME,
DOMAIN,
STABLE_BLE_VERSION_STR,
)
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_PORT
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import issue_registry as ir
from .conftest import MockESPHomeDevice
@@ -113,3 +122,213 @@ async def test_esphome_device_with_current_bluetooth(
)
is None
)
async def test_unique_id_updated_to_mac(
hass: HomeAssistant, mock_client, mock_zeroconf: None
) -> None:
"""Test we update config entry unique ID to MAC address."""
entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: "test.local", CONF_PORT: 6053, CONF_PASSWORD: ""},
unique_id="mock-config-name",
)
entry.add_to_hass(hass)
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(
mac_address="1122334455aa",
)
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.unique_id == "11:22:33:44:55:aa"
async def test_unique_id_not_updated_if_name_same_and_already_mac(
hass: HomeAssistant, mock_client: APIClient, mock_zeroconf: None
) -> None:
"""Test we never update the entry unique ID event if the name is the same."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_HOST: "test.local",
CONF_PORT: 6053,
CONF_PASSWORD: "",
CONF_DEVICE_NAME: "test",
},
unique_id="11:22:33:44:55:aa",
)
entry.add_to_hass(hass)
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(mac_address="1122334455ab", name="test")
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# Mac should never update
assert entry.unique_id == "11:22:33:44:55:aa"
async def test_unique_id_updated_if_name_unset_and_already_mac(
hass: HomeAssistant, mock_client: APIClient, mock_zeroconf: None
) -> None:
"""Test we never update config entry unique ID even if the name is unset."""
entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: "test.local", CONF_PORT: 6053, CONF_PASSWORD: ""},
unique_id="11:22:33:44:55:aa",
)
entry.add_to_hass(hass)
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(mac_address="1122334455ab", name="test")
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# Mac should never update
assert entry.unique_id == "11:22:33:44:55:aa"
async def test_unique_id_not_updated_if_name_different_and_already_mac(
hass: HomeAssistant, mock_client: APIClient, mock_zeroconf: None
) -> None:
"""Test we do not update config entry unique ID if the name is different."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_HOST: "test.local",
CONF_PORT: 6053,
CONF_PASSWORD: "",
CONF_DEVICE_NAME: "test",
},
unique_id="11:22:33:44:55:aa",
)
entry.add_to_hass(hass)
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(mac_address="1122334455ab", name="different")
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
# Mac should not be updated because name is different
assert entry.unique_id == "11:22:33:44:55:aa"
# Name should not be updated either
assert entry.data[CONF_DEVICE_NAME] == "test"
async def test_name_updated_only_if_mac_matches(
hass: HomeAssistant, mock_client: APIClient, mock_zeroconf: None
) -> None:
"""Test we update config entry name only if the mac matches."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_HOST: "test.local",
CONF_PORT: 6053,
CONF_PASSWORD: "",
CONF_DEVICE_NAME: "old",
},
unique_id="11:22:33:44:55:aa",
)
entry.add_to_hass(hass)
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(mac_address="1122334455aa", name="new")
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.unique_id == "11:22:33:44:55:aa"
assert entry.data[CONF_DEVICE_NAME] == "new"
async def test_name_updated_only_if_mac_was_unset(
hass: HomeAssistant, mock_client: APIClient, mock_zeroconf: None
) -> None:
"""Test we update config entry name if the old unique id was not a mac."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_HOST: "test.local",
CONF_PORT: 6053,
CONF_PASSWORD: "",
CONF_DEVICE_NAME: "old",
},
unique_id="notamac",
)
entry.add_to_hass(hass)
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(mac_address="1122334455aa", name="new")
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.unique_id == "11:22:33:44:55:aa"
assert entry.data[CONF_DEVICE_NAME] == "new"
async def test_connection_aborted_wrong_device(
hass: HomeAssistant,
mock_client: APIClient,
mock_zeroconf: None,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test we abort the connection if the unique id is a mac and neither name or mac match."""
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_HOST: "192.168.43.183",
CONF_PORT: 6053,
CONF_PASSWORD: "",
CONF_DEVICE_NAME: "test",
},
unique_id="11:22:33:44:55:aa",
)
entry.add_to_hass(hass)
mock_client.device_info = AsyncMock(
return_value=DeviceInfo(mac_address="1122334455ab", name="different")
)
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert (
"Unexpected device found at 192.168.43.183; expected `test` "
"with mac address `11:22:33:44:55:aa`, found `different` "
"with mac address `11:22:33:44:55:ab`" in caplog.text
)
caplog.clear()
# Make sure discovery triggers a reconnect to the correct device
service_info = dhcp.DhcpServiceInfo(
ip="192.168.43.184",
hostname="test",
macaddress="1122334455aa",
)
new_info = AsyncMock(
return_value=DeviceInfo(mac_address="1122334455aa", name="test")
)
mock_client.device_info = new_info
result = await hass.config_entries.flow.async_init(
"esphome", context={"source": config_entries.SOURCE_DHCP}, data=service_info
)
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "already_configured"
assert entry.data[CONF_HOST] == "192.168.43.184"
await hass.async_block_till_done()
assert len(new_info.mock_calls) == 1
assert "Unexpected device found at" not in caplog.text