mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 09:47:13 +00:00
Add bluetooth adapter model and manufacturer to config flow (#115780)
* Show bluetooth adapter model and manufacturer in config flow If there are multiple adapters, it could be a bit difficult to figure out which one is which * Show bluetooth adapter model and manufacturer in config flow If there are multiple adapters, it could be a bit difficult to figure out which one is which * reorder * reorder * names * remove * fix incomplete mocking * more missing mocks
This commit is contained in:
parent
fbdef7f5cd
commit
53c48537d7
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -11,7 +11,7 @@
|
||||
"description": "[%key:component::bluetooth::config::step::bluetooth_confirm::description%]"
|
||||
}
|
||||
},
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"error": {
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -6,8 +6,10 @@ from typing import Any, cast
|
||||
|
||||
from bluetooth_adapters import (
|
||||
ADAPTER_ADDRESS,
|
||||
ADAPTER_MANUFACTURER,
|
||||
AdapterDetails,
|
||||
adapter_human_name,
|
||||
adapter_model,
|
||||
adapter_unique_name,
|
||||
get_adapters,
|
||||
)
|
||||
@ -35,6 +37,22 @@ OPTIONS_FLOW = {
|
||||
}
|
||||
|
||||
|
||||
def adapter_display_info(adapter: str, details: AdapterDetails) -> str:
|
||||
"""Return the adapter display info."""
|
||||
name = adapter_human_name(adapter, details[ADAPTER_ADDRESS])
|
||||
model = adapter_model(details)
|
||||
manufacturer = details[ADAPTER_MANUFACTURER] or "Unknown"
|
||||
return f"{name} {manufacturer} {model}"
|
||||
|
||||
|
||||
def adapter_title(adapter: str, details: AdapterDetails) -> str:
|
||||
"""Return the adapter title."""
|
||||
unique_name = adapter_unique_name(adapter, details[ADAPTER_ADDRESS])
|
||||
model = adapter_model(details)
|
||||
manufacturer = details[ADAPTER_MANUFACTURER] or "Unknown"
|
||||
return f"{manufacturer} {model} ({unique_name})"
|
||||
|
||||
|
||||
class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Config flow for Bluetooth."""
|
||||
|
||||
@ -45,6 +63,7 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._adapter: str | None = None
|
||||
self._details: AdapterDetails | None = None
|
||||
self._adapters: dict[str, AdapterDetails] = {}
|
||||
self._placeholders: dict[str, str] = {}
|
||||
|
||||
async def async_step_integration_discovery(
|
||||
self, discovery_info: DiscoveryInfoType
|
||||
@ -54,11 +73,23 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._details = cast(AdapterDetails, discovery_info[CONF_DETAILS])
|
||||
await self.async_set_unique_id(self._details[ADAPTER_ADDRESS])
|
||||
self._abort_if_unique_id_configured()
|
||||
self.context["title_placeholders"] = {
|
||||
"name": adapter_human_name(self._adapter, self._details[ADAPTER_ADDRESS])
|
||||
}
|
||||
details = self._details
|
||||
self._async_set_adapter_info(self._adapter, details)
|
||||
return await self.async_step_single_adapter()
|
||||
|
||||
@callback
|
||||
def _async_set_adapter_info(self, adapter: str, details: AdapterDetails) -> None:
|
||||
"""Set the adapter info."""
|
||||
name = adapter_human_name(adapter, details[ADAPTER_ADDRESS])
|
||||
model = adapter_model(details)
|
||||
manufacturer = details[ADAPTER_MANUFACTURER]
|
||||
self._placeholders = {
|
||||
"name": name,
|
||||
"model": model,
|
||||
"manufacturer": manufacturer or "Unknown",
|
||||
}
|
||||
self.context["title_placeholders"] = self._placeholders
|
||||
|
||||
async def async_step_single_adapter(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
@ -67,6 +98,7 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
details = self._details
|
||||
assert adapter is not None
|
||||
assert details is not None
|
||||
assert self._placeholders is not None
|
||||
|
||||
address = details[ADAPTER_ADDRESS]
|
||||
|
||||
@ -74,12 +106,12 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
await self.async_set_unique_id(address, raise_on_progress=False)
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(
|
||||
title=adapter_unique_name(adapter, address), data={}
|
||||
title=adapter_title(adapter, details), data={}
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="single_adapter",
|
||||
description_placeholders={"name": adapter_human_name(adapter, address)},
|
||||
description_placeholders=self._placeholders,
|
||||
)
|
||||
|
||||
async def async_step_multiple_adapters(
|
||||
@ -89,11 +121,12 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
if user_input is not None:
|
||||
assert self._adapters is not None
|
||||
adapter = user_input[CONF_ADAPTER]
|
||||
address = self._adapters[adapter][ADAPTER_ADDRESS]
|
||||
details = self._adapters[adapter]
|
||||
address = details[ADAPTER_ADDRESS]
|
||||
await self.async_set_unique_id(address, raise_on_progress=False)
|
||||
self._abort_if_unique_id_configured()
|
||||
return self.async_create_entry(
|
||||
title=adapter_unique_name(adapter, address), data={}
|
||||
title=adapter_title(adapter, details), data={}
|
||||
)
|
||||
|
||||
configured_addresses = self._async_current_ids()
|
||||
@ -116,6 +149,7 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
if len(unconfigured_adapters) == 1:
|
||||
self._adapter = list(self._adapters)[0]
|
||||
self._details = self._adapters[self._adapter]
|
||||
self._async_set_adapter_info(self._adapter, self._details)
|
||||
return await self.async_step_single_adapter()
|
||||
|
||||
return self.async_show_form(
|
||||
@ -124,8 +158,8 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
{
|
||||
vol.Required(CONF_ADAPTER): vol.In(
|
||||
{
|
||||
adapter: adapter_human_name(
|
||||
adapter, self._adapters[adapter][ADAPTER_ADDRESS]
|
||||
adapter: adapter_display_info(
|
||||
adapter, self._adapters[adapter]
|
||||
)
|
||||
for adapter in sorted(unconfigured_adapters)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "{name}",
|
||||
"flow_title": "{name} {manufacturer} {model}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "Choose a device to set up",
|
||||
@ -18,7 +18,7 @@
|
||||
}
|
||||
},
|
||||
"single_adapter": {
|
||||
"description": "Do you want to set up the Bluetooth adapter {name}?"
|
||||
"description": "Do you want to set up the Bluetooth adapter {name} {manufacturer} {model}?"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "What is the IRK (Identity Resolving Key) of the BLE device you want to track?",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "[%key:component::bluetooth::config::flow_title%]",
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
|
@ -65,7 +65,7 @@ async def test_async_step_user_macos(hass: HomeAssistant, macos_adapter: None) -
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "Core Bluetooth"
|
||||
assert result2["title"] == "Apple Unknown MacOS Model (Core Bluetooth)"
|
||||
assert result2["data"] == {}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
@ -81,6 +81,11 @@ async def test_async_step_user_linux_one_adapter(
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "single_adapter"
|
||||
assert result["description_placeholders"] == {
|
||||
"name": "hci0 (00:00:00:00:00:01)",
|
||||
"model": "Bluetooth Adapter 5.0 (cc01:aa01)",
|
||||
"manufacturer": "ACME",
|
||||
}
|
||||
with (
|
||||
patch("homeassistant.components.bluetooth.async_setup", return_value=True),
|
||||
patch(
|
||||
@ -91,7 +96,9 @@ async def test_async_step_user_linux_one_adapter(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "00:00:00:00:00:01"
|
||||
assert (
|
||||
result2["title"] == "ACME Bluetooth Adapter 5.0 (cc01:aa01) (00:00:00:00:00:01)"
|
||||
)
|
||||
assert result2["data"] == {}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
@ -107,6 +114,10 @@ async def test_async_step_user_linux_two_adapters(
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "multiple_adapters"
|
||||
assert result["data_schema"].schema["adapter"].container == {
|
||||
"hci0": "hci0 (00:00:00:00:00:01) ACME Bluetooth Adapter 5.0 (cc01:aa01)",
|
||||
"hci1": "hci1 (00:00:00:00:00:02) ACME Bluetooth Adapter 5.0 (cc01:aa01)",
|
||||
}
|
||||
with (
|
||||
patch("homeassistant.components.bluetooth.async_setup", return_value=True),
|
||||
patch(
|
||||
@ -117,7 +128,9 @@ async def test_async_step_user_linux_two_adapters(
|
||||
result["flow_id"], user_input={CONF_ADAPTER: "hci1"}
|
||||
)
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "00:00:00:00:00:02"
|
||||
assert (
|
||||
result2["title"] == "ACME Bluetooth Adapter 5.0 (cc01:aa01) (00:00:00:00:00:02)"
|
||||
)
|
||||
assert result2["data"] == {}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
@ -153,6 +166,11 @@ async def test_async_step_integration_discovery(hass: HomeAssistant) -> None:
|
||||
data={CONF_ADAPTER: "hci0", CONF_DETAILS: details},
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["description_placeholders"] == {
|
||||
"name": "hci0 (00:00:00:00:00:01)",
|
||||
"model": "Unknown",
|
||||
"manufacturer": "ACME",
|
||||
}
|
||||
assert result["step_id"] == "single_adapter"
|
||||
with (
|
||||
patch("homeassistant.components.bluetooth.async_setup", return_value=True),
|
||||
@ -164,7 +182,7 @@ async def test_async_step_integration_discovery(hass: HomeAssistant) -> None:
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "00:00:00:00:00:01"
|
||||
assert result2["title"] == "ACME Unknown (00:00:00:00:00:01)"
|
||||
assert result2["data"] == {}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
@ -196,7 +214,7 @@ async def test_async_step_integration_discovery_during_onboarding_one_adapter(
|
||||
data={CONF_ADAPTER: "hci0", CONF_DETAILS: details},
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "00:00:00:00:00:01"
|
||||
assert result["title"] == "ACME Unknown (00:00:00:00:00:01)"
|
||||
assert result["data"] == {}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
assert len(mock_onboarding.mock_calls) == 1
|
||||
@ -240,11 +258,11 @@ async def test_async_step_integration_discovery_during_onboarding_two_adapters(
|
||||
data={CONF_ADAPTER: "hci1", CONF_DETAILS: details2},
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "00:00:00:00:00:01"
|
||||
assert result["title"] == "ACME Unknown (00:00:00:00:00:01)"
|
||||
assert result["data"] == {}
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "00:00:00:00:00:02"
|
||||
assert result2["title"] == "ACME Unknown (00:00:00:00:00:02)"
|
||||
assert result2["data"] == {}
|
||||
|
||||
assert len(mock_setup_entry.mock_calls) == 2
|
||||
@ -278,7 +296,7 @@ async def test_async_step_integration_discovery_during_onboarding(
|
||||
data={CONF_ADAPTER: "Core Bluetooth", CONF_DETAILS: details},
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Core Bluetooth"
|
||||
assert result["title"] == "ACME Unknown (Core Bluetooth)"
|
||||
assert result["data"] == {}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
assert len(mock_onboarding.mock_calls) == 1
|
||||
|
@ -3015,12 +3015,14 @@ async def test_discover_new_usb_adapters(
|
||||
"hw_version": "usb:v1D6Bp0246d053F",
|
||||
"passive_scan": False,
|
||||
"sw_version": "homeassistant",
|
||||
"manufacturer": "ACME",
|
||||
},
|
||||
"hci1": {
|
||||
"address": "00:00:00:00:00:02",
|
||||
"hw_version": "usb:v1D6Bp0246d053F",
|
||||
"passive_scan": False,
|
||||
"sw_version": "homeassistant",
|
||||
"manufacturer": "ACME",
|
||||
},
|
||||
},
|
||||
),
|
||||
@ -3088,12 +3090,14 @@ async def test_discover_new_usb_adapters_with_firmware_fallback_delay(
|
||||
"hw_version": "usb:v1D6Bp0246d053F",
|
||||
"passive_scan": False,
|
||||
"sw_version": "homeassistant",
|
||||
"manufacturer": "ACME",
|
||||
},
|
||||
"hci1": {
|
||||
"address": "00:00:00:00:00:02",
|
||||
"hw_version": "usb:v1D6Bp0246d053F",
|
||||
"passive_scan": False,
|
||||
"sw_version": "homeassistant",
|
||||
"manufacturer": "ACME",
|
||||
},
|
||||
},
|
||||
),
|
||||
|
Loading…
x
Reference in New Issue
Block a user