mirror of
https://github.com/home-assistant/core.git
synced 2025-07-15 09:17: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."""
|
||||
from __future__ import annotations
|
||||
|
||||
import platform
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
import voluptuous as vol
|
||||
@ -11,6 +10,7 @@ from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.typing import DiscoveryInfoType
|
||||
|
||||
from . import models
|
||||
from .const import (
|
||||
ADAPTER_ADDRESS,
|
||||
CONF_ADAPTER,
|
||||
@ -134,7 +134,7 @@ class BluetoothConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
@callback
|
||||
def async_supports_options_flow(cls, config_entry: ConfigEntry) -> bool:
|
||||
"""Return options flow support for this handler."""
|
||||
return platform.system() == "Linux"
|
||||
return bool(models.MANAGER and models.MANAGER.supports_passive_scan)
|
||||
|
||||
|
||||
class OptionsFlowHandler(OptionsFlow):
|
||||
|
@ -59,8 +59,10 @@ class AdapterDetails(TypedDict, total=False):
|
||||
address: str
|
||||
sw_version: str
|
||||
hw_version: str
|
||||
passive_scan: bool
|
||||
|
||||
|
||||
ADAPTER_ADDRESS: Final = "address"
|
||||
ADAPTER_SW_VERSION: Final = "sw_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 (
|
||||
ADAPTER_ADDRESS,
|
||||
ADAPTER_PASSIVE_SCAN,
|
||||
STALE_ADVERTISEMENT_SECONDS,
|
||||
UNAVAILABLE_TRACK_SECONDS,
|
||||
AdapterDetails,
|
||||
@ -147,6 +148,11 @@ class BluetoothManager:
|
||||
self._connectable_scanners: list[BaseHaScanner] = []
|
||||
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]:
|
||||
"""Diagnostics for the manager."""
|
||||
scanner_diagnostics = await asyncio.gather(
|
||||
|
@ -29,9 +29,8 @@
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"description": "Passive listening requires BlueZ 5.63 or later with experimental features enabled.",
|
||||
"data": {
|
||||
"passive": "Passive listening"
|
||||
"passive": "Passive scanning"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,9 +9,6 @@
|
||||
"bluetooth_confirm": {
|
||||
"description": "Do you want to setup {name}?"
|
||||
},
|
||||
"enable_bluetooth": {
|
||||
"description": "Do you want to setup Bluetooth?"
|
||||
},
|
||||
"multiple_adapters": {
|
||||
"data": {
|
||||
"adapter": "Adapter"
|
||||
@ -33,10 +30,8 @@
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"adapter": "The Bluetooth Adapter to use for scanning",
|
||||
"passive": "Passive listening"
|
||||
},
|
||||
"description": "Passive listening requires BlueZ 5.63 or later with experimental features enabled."
|
||||
"passive": "Passive scanning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]:
|
||||
WINDOWS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails(
|
||||
address=DEFAULT_ADDRESS,
|
||||
sw_version=platform.release(),
|
||||
passive_scan=False,
|
||||
)
|
||||
}
|
||||
if platform.system() == "Darwin":
|
||||
@ -31,6 +32,7 @@ async def async_get_bluetooth_adapters() -> dict[str, AdapterDetails]:
|
||||
MACOS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails(
|
||||
address=DEFAULT_ADDRESS,
|
||||
sw_version=platform.release(),
|
||||
passive_scan=False,
|
||||
)
|
||||
}
|
||||
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"],
|
||||
sw_version=adapter1["Name"], # This is actually the BlueZ version
|
||||
hw_version=adapter1["Modalias"],
|
||||
passive_scan="org.bluez.AdvertisementMonitorManager1" in details,
|
||||
)
|
||||
return adapters
|
||||
|
||||
|
@ -42,7 +42,11 @@ def one_adapter_fixture():
|
||||
"Address": "00:00:00:00:00:01",
|
||||
"Name": "BlueZ 4.63",
|
||||
"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",
|
||||
"Name": "BlueZ 4.63",
|
||||
"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
|
||||
|
||||
|
||||
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):
|
||||
"""Test setting up manually with one adapter on MacOS."""
|
||||
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
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.components.bluetooth.config_flow.platform.system",
|
||||
return_value="Darwin",
|
||||
)
|
||||
async def test_options_flow_disabled_macos(mock_system, hass, hass_ws_client):
|
||||
async def test_options_flow_disabled_macos(
|
||||
hass, hass_ws_client, mock_bleak_scanner_start, macos_adapter
|
||||
):
|
||||
"""Test options are disabled on MacOS."""
|
||||
await async_setup_component(hass, "config", {})
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={},
|
||||
options={},
|
||||
domain=DOMAIN, data={}, options={}, unique_id=DEFAULT_ADDRESS
|
||||
)
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
|
||||
@patch(
|
||||
"homeassistant.components.bluetooth.config_flow.platform.system",
|
||||
return_value="Linux",
|
||||
)
|
||||
async def test_options_flow_enabled_linux(mock_system, hass, hass_ws_client):
|
||||
async def test_options_flow_enabled_linux(
|
||||
hass, hass_ws_client, mock_bleak_scanner_start, one_adapter
|
||||
):
|
||||
"""Test options are enabled on Linux."""
|
||||
await async_setup_component(hass, "config", {})
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={},
|
||||
options={},
|
||||
unique_id="00:00:00:00:00:01",
|
||||
)
|
||||
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)
|
||||
|
||||
await ws_client.send_json(
|
||||
|
@ -31,7 +31,16 @@ async def test_diagnostics(
|
||||
return_value={
|
||||
"org.bluez": {
|
||||
"/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": {
|
||||
"address": "00:00:00:00:00:01",
|
||||
"hw_version": "usbid:1234",
|
||||
"passive_scan": False,
|
||||
"sw_version": "BlueZ 4.63",
|
||||
},
|
||||
"hci1": {
|
||||
"address": "00:00:00:00:00:02",
|
||||
"hw_version": "usbid:1234",
|
||||
"passive_scan": True,
|
||||
"sw_version": "BlueZ 4.63",
|
||||
},
|
||||
},
|
||||
"dbus": {
|
||||
"org.bluez": {
|
||||
"/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": {
|
||||
"address": "00:00:00:00:00:01",
|
||||
"hw_version": "usbid:1234",
|
||||
"passive_scan": False,
|
||||
"sw_version": "BlueZ 4.63",
|
||||
},
|
||||
"hci1": {
|
||||
"address": "00:00:00:00:00:02",
|
||||
"hw_version": "usbid:1234",
|
||||
"passive_scan": True,
|
||||
"sw_version": "BlueZ 4.63",
|
||||
},
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user