Skip to content

Commit

Permalink
Remove unignore flow
Browse files Browse the repository at this point in the history
  • Loading branch information
emontnemery committed Sep 25, 2024
1 parent 3810c3c commit 43642a8
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 705 deletions.
18 changes: 0 additions & 18 deletions homeassistant/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -1855,20 +1855,6 @@ async def async_remove(self, entry_id: str) -> dict[str, Any]:
issue_id = f"config_entry_reauth_{entry.domain}_{entry.entry_id}"
ir.async_delete_issue(self.hass, HOMEASSISTANT_DOMAIN, issue_id)

# After we have fully removed an "ignore" config entry we can try and rediscover
# it so that a user is able to immediately start configuring it. We do this by
# starting a new flow with the 'unignore' step. If the integration doesn't
# implement async_step_unignore then this will be a no-op.
if entry.source == SOURCE_IGNORE:
self.hass.async_create_task_internal(
self.hass.config_entries.flow.async_init(
entry.domain,
context={"source": SOURCE_UNIGNORE},
data={"unique_id": entry.unique_id},
),
f"config entry unignore {entry.title} {entry.domain} {entry.unique_id}",
)

self._async_dispatch(ConfigEntryChange.REMOVED, entry)
for discovery_domain in entry.discovery_keys:
async_dispatcher_send_internal(
Expand Down Expand Up @@ -2544,10 +2530,6 @@ async def async_step_ignore(self, user_input: dict[str, Any]) -> ConfigFlowResul
await self.async_set_unique_id(user_input["unique_id"], raise_on_progress=False)
return self.async_create_entry(title=user_input["title"], data={})

async def async_step_unignore(self, user_input: dict[str, Any]) -> ConfigFlowResult:
"""Rediscover a config entry by it's unique_id."""
return self.async_abort(reason="not_implemented")

async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
Expand Down
211 changes: 7 additions & 204 deletions tests/components/bluetooth/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1335,213 +1335,21 @@ def _switchbot_device_unavailable_callback(_address: str) -> None:
),
],
)
@pytest.mark.parametrize("entry_source", [config_entries.SOURCE_IGNORE])
async def test_bluetooth_rediscover(
hass: HomeAssistant,
entry_domain: str,
entry_discovery_keys: tuple,
entry_source: str,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed."""
mock_bt = [
{
"domain": "switchbot",
"service_data_uuid": "050a021a-0000-1000-8000-00805f9b34fb",
"connectable": False,
},
]
with patch(
"homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt
):
assert await async_setup_component(hass, bluetooth.DOMAIN, {})
await hass.async_block_till_done()

assert async_scanner_count(hass, connectable=False) == 0
switchbot_device_non_connectable = generate_ble_device(
"44:44:33:11:23:45",
"wohand",
{},
rssi=-100,
)
switchbot_device_adv = generate_advertisement_data(
local_name="wohand",
service_uuids=["050a021a-0000-1000-8000-00805f9b34fb"],
service_data={"050a021a-0000-1000-8000-00805f9b34fb": b"\n\xff"},
manufacturer_data={1: b"\x01"},
rssi=-100,
)
callbacks = []

def _fake_subscriber(
service_info: BluetoothServiceInfo,
change: BluetoothChange,
) -> None:
"""Fake subscriber for the BleakScanner."""
callbacks.append((service_info, change))

cancel = bluetooth.async_register_callback(
hass,
_fake_subscriber,
{"address": "44:44:33:11:23:45", "connectable": False},
BluetoothScanningMode.ACTIVE,
)

class FakeScanner(BaseHaRemoteScanner):
def inject_advertisement(
self, device: BLEDevice, advertisement_data: AdvertisementData
) -> None:
"""Inject an advertisement."""
self._async_on_advertisement(
device.address,
advertisement_data.rssi,
device.name,
advertisement_data.service_uuids,
advertisement_data.service_data,
advertisement_data.manufacturer_data,
advertisement_data.tx_power,
{"scanner_specific_data": "test"},
MONOTONIC_TIME(),
)

def clear_all_devices(self) -> None:
"""Clear all devices."""
self._discovered_device_advertisement_datas.clear()
self._discovered_device_timestamps.clear()
self._previous_service_info.clear()

connector = (
HaBluetoothConnector(MockBleakClient, "mock_bleak_client", lambda: False),
)
non_connectable_scanner = FakeScanner(
"connectable",
"connectable",
connector,
False,
)
unsetup_connectable_scanner = non_connectable_scanner.async_setup()
cancel_connectable_scanner = _get_manager().async_register_scanner(
non_connectable_scanner
)
with patch.object(hass.config_entries.flow, "async_init") as mock_config_flow:
non_connectable_scanner.inject_advertisement(
switchbot_device_non_connectable, switchbot_device_adv
)
await hass.async_block_till_done()

expected_context = {
"discovery_key": DiscoveryKey(
domain="bluetooth", key="44:44:33:11:23:45", version=1
),
"source": "bluetooth",
}
assert len(mock_config_flow.mock_calls) == 1
assert mock_config_flow.mock_calls[0][1][0] == "switchbot"
assert mock_config_flow.mock_calls[0][2]["context"] == expected_context

hass.config.components.add(entry_domain)
mock_integration(hass, MockModule(entry_domain))

entry = MockConfigEntry(
domain=entry_domain,
discovery_keys=entry_discovery_keys,
unique_id="mock-unique-id",
state=config_entries.ConfigEntryState.LOADED,
source=entry_source,
)
entry.add_to_hass(hass)

assert (
async_ble_device_from_address(hass, "44:44:33:11:23:45", False) is not None
)
assert async_scanner_count(hass, connectable=False) == 1
assert len(callbacks) == 1

assert (
"44:44:33:11:23:45"
in non_connectable_scanner.discovered_devices_and_advertisement_data
)

await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()

assert (
async_ble_device_from_address(hass, "44:44:33:11:23:45", False) is not None
)
assert async_scanner_count(hass, connectable=False) == 1
assert len(callbacks) == 1

assert len(mock_config_flow.mock_calls) == 3
assert mock_config_flow.mock_calls[1][1][0] == entry_domain
assert mock_config_flow.mock_calls[1][2]["context"] == {
"source": "unignore",
}
assert mock_config_flow.mock_calls[2][1][0] == "switchbot"
assert mock_config_flow.mock_calls[2][2]["context"] == expected_context

cancel()
unsetup_connectable_scanner()
cancel_connectable_scanner()


@pytest.mark.usefixtures("mock_bluetooth_adapters")
@pytest.mark.parametrize(
(
"entry_domain",
"entry_discovery_keys",
),
"entry_source",
[
# Matching discovery key
(
"switchbot",
{
"bluetooth": (
DiscoveryKey(
domain="bluetooth", key="44:44:33:11:23:45", version=1
),
)
},
),
# Matching discovery key
(
"switchbot",
{
"bluetooth": (
DiscoveryKey(
domain="bluetooth", key="44:44:33:11:23:45", version=1
),
),
"other": (DiscoveryKey(domain="other", key="blah", version=1),),
},
),
# Matching discovery key, other domain
# Note: Rediscovery is not currently restricted to the domain of the removed
# entry. Such a check can be added if needed.
(
"comp",
{
"bluetooth": (
DiscoveryKey(
domain="bluetooth", key="44:44:33:11:23:45", version=1
),
)
},
),
config_entries.SOURCE_BLUETOOTH,
config_entries.SOURCE_IGNORE,
config_entries.SOURCE_USER,
],
)
@pytest.mark.parametrize(
"entry_source", [config_entries.SOURCE_BLUETOOTH, config_entries.SOURCE_USER]
)
async def test_bluetooth_rediscover_2(
async def test_bluetooth_rediscover(
hass: HomeAssistant,
entry_domain: str,
entry_discovery_keys: tuple,
entry_source: str,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed.
This test can be merged with test_zeroconf_rediscover when
async_step_unignore has been removed from the ConfigFlow base class.
"""
"""Test we reinitiate flows when an ignored config entry is removed."""
mock_bt = [
{
"domain": "switchbot",
Expand Down Expand Up @@ -1847,12 +1655,7 @@ def clear_all_devices(self) -> None:
)
assert async_scanner_count(hass, connectable=False) == 1
assert len(callbacks) == 1

assert len(mock_config_flow.mock_calls) == 2
assert mock_config_flow.mock_calls[1][1][0] == entry_domain
assert mock_config_flow.mock_calls[1][2]["context"] == {
"source": "unignore",
}
assert len(mock_config_flow.mock_calls) == 1

cancel()
unsetup_connectable_scanner()
Expand Down
108 changes: 7 additions & 101 deletions tests/components/dhcp/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -1201,113 +1201,21 @@ async def test_aiodiscover_finds_new_hosts_after_interval(hass: HomeAssistant) -
),
],
)
@pytest.mark.parametrize("entry_source", [config_entries.SOURCE_IGNORE])
async def test_dhcp_rediscover(
hass: HomeAssistant,
entry_domain: str,
entry_discovery_keys: tuple,
entry_source: str,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed."""

entry = MockConfigEntry(
domain=entry_domain,
discovery_keys=entry_discovery_keys,
unique_id="mock-unique-id",
state=config_entries.ConfigEntryState.LOADED,
source=entry_source,
)
entry.add_to_hass(hass)

address_data = {}
integration_matchers = dhcp.async_index_integration_matchers(
[{"domain": "mock-domain", "hostname": "connect", "macaddress": "B8B7F1*"}]
)
packet = Ether(RAW_DHCP_REQUEST)

async_handle_dhcp_packet = await _async_get_handle_dhcp_packet(
hass, integration_matchers, address_data
)
rediscovery_watcher = dhcp.RediscoveryWatcher(
hass, address_data, integration_matchers
)
rediscovery_watcher.async_start()
with patch.object(hass.config_entries.flow, "async_init") as mock_init:
await async_handle_dhcp_packet(packet)
# Ensure no change is ignored
await async_handle_dhcp_packet(packet)

# Assert the cached MAC address is hexstring without :
assert address_data == {
"b8b7f16db533": {"hostname": "connect", "ip": "192.168.210.56"}
}

expected_context = {
"discovery_key": DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),
"source": config_entries.SOURCE_DHCP,
}
assert len(mock_init.mock_calls) == 1
assert mock_init.mock_calls[0][1][0] == "mock-domain"
assert mock_init.mock_calls[0][2]["context"] == expected_context
assert mock_init.mock_calls[0][2]["data"] == dhcp.DhcpServiceInfo(
ip="192.168.210.56",
hostname="connect",
macaddress="b8b7f16db533",
)

with patch.object(hass.config_entries.flow, "async_init") as mock_init:
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()

assert len(mock_init.mock_calls) == 2
assert mock_init.mock_calls[0][1][0] == entry_domain
assert mock_init.mock_calls[0][2]["context"] == {"source": "unignore"}
assert mock_init.mock_calls[1][1][0] == "mock-domain"
assert mock_init.mock_calls[1][2]["context"] == expected_context


@pytest.mark.parametrize(
(
"entry_domain",
"entry_discovery_keys",
),
"entry_source",
[
# Matching discovery key
(
"mock-domain",
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),)},
),
# Matching discovery key
(
"mock-domain",
{
"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),),
"other": (DiscoveryKey(domain="other", key="blah", version=1),),
},
),
# Matching discovery key, other domain
# Note: Rediscovery is not currently restricted to the domain of the removed
# entry. Such a check can be added if needed.
(
"comp",
{"dhcp": (DiscoveryKey(domain="dhcp", key="b8b7f16db533", version=1),)},
),
config_entries.SOURCE_DHCP,
config_entries.SOURCE_IGNORE,
config_entries.SOURCE_USER,
],
)
@pytest.mark.parametrize(
"entry_source", [config_entries.SOURCE_USER, config_entries.SOURCE_ZEROCONF]
)
async def test_dhcp_rediscover_2(
async def test_dhcp_rediscover(
hass: HomeAssistant,
entry_domain: str,
entry_discovery_keys: tuple,
entry_source: str,
) -> None:
"""Test we reinitiate flows when an ignored config entry is removed.
This test can be merged with test_zeroconf_rediscover when
async_step_unignore has been removed from the ConfigFlow base class.
"""
"""Test we reinitiate flows when an ignored config entry is removed."""

entry = MockConfigEntry(
domain=entry_domain,
Expand Down Expand Up @@ -1447,6 +1355,4 @@ async def test_dhcp_rediscover_no_match(
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()

assert len(mock_init.mock_calls) == 1
assert mock_init.mock_calls[0][1][0] == entry_domain
assert mock_init.mock_calls[0][2]["context"] == {"source": "unignore"}
assert len(mock_init.mock_calls) == 0
Loading

0 comments on commit 43642a8

Please sign in to comment.