mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 17:27:10 +00:00
Hide bluetooth passive option if its not available on the host system (#77421)
* Hide bluetooth passive option if its not available - We now have a way to determine in advance if passive scanning is supported by BlueZ * drop string
This commit is contained in:
parent
d25a76d3d6
commit
a28aeeeca7
@ -1,7 +1,6 @@
|
|||||||
"""Config flow to configure the Bluetooth integration."""
|
"""Config flow to configure the Bluetooth integration."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import platform
|
|
||||||
from typing import TYPE_CHECKING, Any, cast
|
from typing import TYPE_CHECKING, Any, cast
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@ -11,6 +10,7 @@ from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
|
|||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||||
|
|
||||||
|
from . import models
|
||||||
from .const import (
|
from .const import (
|
||||||
ADAPTER_ADDRESS,
|
ADAPTER_ADDRESS,
|
||||||
CONF_ADAPTER,
|
CONF_ADAPTER,
|
||||||
@ -134,7 +134,7 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
@callback
|
@callback
|
||||||
def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
|
def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
|
||||||
"""Return options flow support for this handler."""
|
"""Return options flow support for this handler."""
|
||||||
return platform.system() == "Linux"
|
return bool(models.MANAGER and models.MANAGER.supports_passive_scan)
|
||||||
|
|
||||||
|
|
||||||
class OptionsFlowHandler(OptionsFlow):
|
class OptionsFlowHandler(OptionsFlow):
|
||||||
|
@ -59,8 +59,10 @@ class AdapterDetails(TypedDict, total=False):
|
|||||||
address: str
|
address: str
|
||||||
sw_version: str
|
sw_version: str
|
||||||
hw_version: str
|
hw_version: str
|
||||||
|
passive_scan: bool
|
||||||
|
|
||||||
|
|
||||||
ADAPTER_ADDRESS: Final = "address"
|
ADAPTER_ADDRESS: Final = "address"
|
||||||
ADAPTER_SW_VERSION: Final = "sw_version"
|
ADAPTER_SW_VERSION: Final = "sw_version"
|
||||||
ADAPTER_HW_VERSION: Final = "hw_version"
|
ADAPTER_HW_VERSION: Final = "hw_version"
|
||||||
|
ADAPTER_PASSIVE_SCAN: Final = "passive_scan"
|
||||||
|
@ -23,6 +23,7 @@ from homeassistant.helpers.event import async_track_time_interval
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
ADAPTER_ADDRESS,
|
ADAPTER_ADDRESS,
|
||||||
|
ADAPTER_PASSIVE_SCAN,
|
||||||
STALE_ADVERTISEMENT_SECONDS,
|
STALE_ADVERTISEMENT_SECONDS,
|
||||||
UNAVAILABLE_TRACK_SECONDS,
|
UNAVAILABLE_TRACK_SECONDS,
|
||||||
AdapterDetails,
|
AdapterDetails,
|
||||||
@ -147,6 +148,11 @@ class BluetoothManager:
|
|||||||
self._connectable_scanners: list[BaseHaScanner] = []
|
self._connectable_scanners: list[BaseHaScanner] = []
|
||||||
self._adapters: dict[str, AdapterDetails] = {}
|
self._adapters: dict[str, AdapterDetails] = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supports_passive_scan(self) -> bool:
|
||||||
|
"""Return if passive scan is supported."""
|
||||||
|
return any(adapter[ADAPTER_PASSIVE_SCAN] for adapter in self._adapters.values())
|
||||||
|
|
||||||
async def async_diagnostics(self) -> dict[str, Any]:
|
async def async_diagnostics(self) -> dict[str, Any]:
|
||||||
"""Diagnostics for the manager."""
|
"""Diagnostics for the manager."""
|
||||||
scanner_diagnostics = await asyncio.gather(
|
scanner_diagnostics = await asyncio.gather(
|
||||||
|
@ -29,9 +29,8 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
"description": "Passive listening requires BlueZ 5.63 or later with experimental features enabled.",
|
|
||||||
"data": {
|
"data": {
|
||||||
"passive": "Passive listening"
|
"passive": "Passive scanning"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,6 @@
|
|||||||
"bluetooth_confirm": {
|
"bluetooth_confirm": {
|
||||||
"description": "Do you want to setup {name}?"
|
"description": "Do you want to setup {name}?"
|
||||||
},
|
},
|
||||||
"enable_bluetooth": {
|
|
||||||
"description": "Do you want to setup Bluetooth?"
|
|
||||||
},
|
|
||||||
"multiple_adapters": {
|
"multiple_adapters": {
|
||||||
"data": {
|
"data": {
|
||||||
"adapter": "Adapter"
|
"adapter": "Adapter"
|
||||||
@ -33,10 +30,8 @@
|
|||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
"data": {
|
"data": {
|
||||||
"adapter": "The Bluetooth Adapter to use for scanning",
|
"passive": "Passive scanning"
|
||||||
"passive": "Passive listening"
|
}
|
||||||
},
|
|
||||||
"description": "Passive listening requires BlueZ 5.63 or later with experimental features enabled."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]:
|
|||||||
WINDOWS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails(
|
WINDOWS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails(
|
||||||
address=DEFAULT_ADDRESS,
|
address=DEFAULT_ADDRESS,
|
||||||
sw_version=platform.release(),
|
sw_version=platform.release(),
|
||||||
|
passive_scan=False,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
@ -31,6 +32,7 @@ async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]:
|
|||||||
MACOS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails(
|
MACOS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails(
|
||||||
address=DEFAULT_ADDRESS,
|
address=DEFAULT_ADDRESS,
|
||||||
sw_version=platform.release(),
|
sw_version=platform.release(),
|
||||||
|
passive_scan=False,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
from bluetooth_adapters import ( # pylint: disable=import-outside-toplevel
|
from bluetooth_adapters import ( # pylint: disable=import-outside-toplevel
|
||||||
@ -45,6 +47,7 @@ async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]:
|
|||||||
address=adapter1["Address"],
|
address=adapter1["Address"],
|
||||||
sw_version=adapter1["Name"], # This is actually the BlueZ version
|
sw_version=adapter1["Name"], # This is actually the BlueZ version
|
||||||
hw_version=adapter1["Modalias"],
|
hw_version=adapter1["Modalias"],
|
||||||
|
passive_scan="org.bluez.AdvertisementMonitorManager1" in details,
|
||||||
)
|
)
|
||||||
return adapters
|
return adapters
|
||||||
|
|
||||||
|
@ -42,7 +42,11 @@ def one_adapter_fixture():
|
|||||||
"Address": "00:00:00:00:00:01",
|
"Address": "00:00:00:00:00:01",
|
||||||
"Name": "BlueZ 4.63",
|
"Name": "BlueZ 4.63",
|
||||||
"Modalias": "usbid:1234",
|
"Modalias": "usbid:1234",
|
||||||
}
|
},
|
||||||
|
"org.bluez.AdvertisementMonitorManager1": {
|
||||||
|
"SupportedMonitorTypes": ["or_patterns"],
|
||||||
|
"SupportedFeatures": [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
):
|
):
|
||||||
@ -74,7 +78,11 @@ def two_adapters_fixture():
|
|||||||
"Address": "00:00:00:00:00:02",
|
"Address": "00:00:00:00:00:02",
|
||||||
"Name": "BlueZ 4.63",
|
"Name": "BlueZ 4.63",
|
||||||
"Modalias": "usbid:1234",
|
"Modalias": "usbid:1234",
|
||||||
}
|
},
|
||||||
|
"org.bluez.AdvertisementMonitorManager1": {
|
||||||
|
"SupportedMonitorTypes": ["or_patterns"],
|
||||||
|
"SupportedFeatures": [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
):
|
):
|
||||||
|
@ -17,6 +17,29 @@ from homeassistant.setup import async_setup_component
|
|||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options_flow_disabled_not_setup(
|
||||||
|
hass, hass_ws_client, mock_bleak_scanner_start, macos_adapter
|
||||||
|
):
|
||||||
|
"""Test options are disabled if the integration has not been setup."""
|
||||||
|
await async_setup_component(hass, "config", {})
|
||||||
|
entry = MockConfigEntry(
|
||||||
|
domain=DOMAIN, data={}, options={}, unique_id=DEFAULT_ADDRESS
|
||||||
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
ws_client = await hass_ws_client(hass)
|
||||||
|
|
||||||
|
await ws_client.send_json(
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"type": "config_entries/get",
|
||||||
|
"domain": "bluetooth",
|
||||||
|
"type_filter": "integration",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
response = await ws_client.receive_json()
|
||||||
|
assert response["result"][0]["supports_options"] is False
|
||||||
|
|
||||||
|
|
||||||
async def test_async_step_user_macos(hass, macos_adapter):
|
async def test_async_step_user_macos(hass, macos_adapter):
|
||||||
"""Test setting up manually with one adapter on MacOS."""
|
"""Test setting up manually with one adapter on MacOS."""
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
@ -287,19 +310,18 @@ async def test_options_flow_linux(hass, mock_bleak_scanner_start, one_adapter):
|
|||||||
assert result["data"][CONF_PASSIVE] is False
|
assert result["data"][CONF_PASSIVE] is False
|
||||||
|
|
||||||
|
|
||||||
@patch(
|
async def test_options_flow_disabled_macos(
|
||||||
"homeassistant.components.bluetooth.config_flow.platform.system",
|
hass, hass_ws_client, mock_bleak_scanner_start, macos_adapter
|
||||||
return_value="Darwin",
|
):
|
||||||
)
|
|
||||||
async def test_options_flow_disabled_macos(mock_system, hass, hass_ws_client):
|
|
||||||
"""Test options are disabled on MacOS."""
|
"""Test options are disabled on MacOS."""
|
||||||
await async_setup_component(hass, "config", {})
|
await async_setup_component(hass, "config", {})
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN, data={}, options={}, unique_id=DEFAULT_ADDRESS
|
||||||
data={},
|
|
||||||
options={},
|
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
ws_client = await hass_ws_client(hass)
|
ws_client = await hass_ws_client(hass)
|
||||||
|
|
||||||
await ws_client.send_json(
|
await ws_client.send_json(
|
||||||
@ -314,19 +336,21 @@ async def test_options_flow_disabled_macos(mock_system, hass, hass_ws_client):
|
|||||||
assert response["result"][0]["supports_options"] is False
|
assert response["result"][0]["supports_options"] is False
|
||||||
|
|
||||||
|
|
||||||
@patch(
|
async def test_options_flow_enabled_linux(
|
||||||
"homeassistant.components.bluetooth.config_flow.platform.system",
|
hass, hass_ws_client, mock_bleak_scanner_start, one_adapter
|
||||||
return_value="Linux",
|
):
|
||||||
)
|
|
||||||
async def test_options_flow_enabled_linux(mock_system, hass, hass_ws_client):
|
|
||||||
"""Test options are enabled on Linux."""
|
"""Test options are enabled on Linux."""
|
||||||
await async_setup_component(hass, "config", {})
|
await async_setup_component(hass, "config", {})
|
||||||
entry = MockConfigEntry(
|
entry = MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={},
|
data={},
|
||||||
options={},
|
options={},
|
||||||
|
unique_id="00:00:00:00:00:01",
|
||||||
)
|
)
|
||||||
entry.add_to_hass(hass)
|
entry.add_to_hass(hass)
|
||||||
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
ws_client = await hass_ws_client(hass)
|
ws_client = await hass_ws_client(hass)
|
||||||
|
|
||||||
await ws_client.send_json(
|
await ws_client.send_json(
|
||||||
|
@ -31,7 +31,16 @@ async def test_diagnostics(
|
|||||||
return_value={
|
return_value={
|
||||||
"org.bluez": {
|
"org.bluez": {
|
||||||
"/org/bluez/hci0": {
|
"/org/bluez/hci0": {
|
||||||
"Interfaces": {"org.bluez.Adapter1": {"Discovering": False}}
|
"org.bluez.Adapter1": {
|
||||||
|
"Name": "BlueZ 5.63",
|
||||||
|
"Alias": "BlueZ 5.63",
|
||||||
|
"Modalias": "usb:v1D6Bp0246d0540",
|
||||||
|
"Discovering": False,
|
||||||
|
},
|
||||||
|
"org.bluez.AdvertisementMonitorManager1": {
|
||||||
|
"SupportedMonitorTypes": ["or_patterns"],
|
||||||
|
"SupportedFeatures": [],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -57,18 +66,29 @@ async def test_diagnostics(
|
|||||||
"hci0": {
|
"hci0": {
|
||||||
"address": "00:00:00:00:00:01",
|
"address": "00:00:00:00:00:01",
|
||||||
"hw_version": "usbid:1234",
|
"hw_version": "usbid:1234",
|
||||||
|
"passive_scan": False,
|
||||||
"sw_version": "BlueZ 4.63",
|
"sw_version": "BlueZ 4.63",
|
||||||
},
|
},
|
||||||
"hci1": {
|
"hci1": {
|
||||||
"address": "00:00:00:00:00:02",
|
"address": "00:00:00:00:00:02",
|
||||||
"hw_version": "usbid:1234",
|
"hw_version": "usbid:1234",
|
||||||
|
"passive_scan": True,
|
||||||
"sw_version": "BlueZ 4.63",
|
"sw_version": "BlueZ 4.63",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"dbus": {
|
"dbus": {
|
||||||
"org.bluez": {
|
"org.bluez": {
|
||||||
"/org/bluez/hci0": {
|
"/org/bluez/hci0": {
|
||||||
"Interfaces": {"org.bluez.Adapter1": {"Discovering": False}}
|
"org.bluez.Adapter1": {
|
||||||
|
"Alias": "BlueZ " "5.63",
|
||||||
|
"Discovering": False,
|
||||||
|
"Modalias": "usb:v1D6Bp0246d0540",
|
||||||
|
"Name": "BlueZ " "5.63",
|
||||||
|
},
|
||||||
|
"org.bluez.AdvertisementMonitorManager1": {
|
||||||
|
"SupportedFeatures": [],
|
||||||
|
"SupportedMonitorTypes": ["or_patterns"],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -77,11 +97,13 @@ async def test_diagnostics(
|
|||||||
"hci0": {
|
"hci0": {
|
||||||
"address": "00:00:00:00:00:01",
|
"address": "00:00:00:00:00:01",
|
||||||
"hw_version": "usbid:1234",
|
"hw_version": "usbid:1234",
|
||||||
|
"passive_scan": False,
|
||||||
"sw_version": "BlueZ 4.63",
|
"sw_version": "BlueZ 4.63",
|
||||||
},
|
},
|
||||||
"hci1": {
|
"hci1": {
|
||||||
"address": "00:00:00:00:00:02",
|
"address": "00:00:00:00:00:02",
|
||||||
"hw_version": "usbid:1234",
|
"hw_version": "usbid:1234",
|
||||||
|
"passive_scan": True,
|
||||||
"sw_version": "BlueZ 4.63",
|
"sw_version": "BlueZ 4.63",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user