mirror of
https://github.com/home-assistant/core.git
synced 2025-07-10 14:57:09 +00:00
Make BT support detection dynamic for Shelly RPC devices (#137323)
This commit is contained in:
parent
208e8ae451
commit
f4fa4056ac
@ -293,6 +293,8 @@ async def _async_setup_rpc_entry(hass: HomeAssistant, entry: ShellyConfigEntry)
|
|||||||
translation_key="firmware_unsupported",
|
translation_key="firmware_unsupported",
|
||||||
translation_placeholders={"device": entry.title},
|
translation_placeholders={"device": entry.title},
|
||||||
)
|
)
|
||||||
|
runtime_data.rpc_supports_scripts = await device.supports_scripts()
|
||||||
|
if runtime_data.rpc_supports_scripts:
|
||||||
runtime_data.rpc_script_events = await get_rpc_scripts_event_types(
|
runtime_data.rpc_script_events = await get_rpc_scripts_event_types(
|
||||||
device, ignore_scripts=[BLE_SCRIPT_NAME]
|
device, ignore_scripts=[BLE_SCRIPT_NAME]
|
||||||
)
|
)
|
||||||
|
@ -7,12 +7,7 @@ from typing import Any, Final
|
|||||||
|
|
||||||
from aioshelly.block_device import BlockDevice
|
from aioshelly.block_device import BlockDevice
|
||||||
from aioshelly.common import ConnectionOptions, get_info
|
from aioshelly.common import ConnectionOptions, get_info
|
||||||
from aioshelly.const import (
|
from aioshelly.const import BLOCK_GENERATIONS, DEFAULT_HTTP_PORT, RPC_GENERATIONS
|
||||||
BLOCK_GENERATIONS,
|
|
||||||
DEFAULT_HTTP_PORT,
|
|
||||||
MODEL_WALL_DISPLAY,
|
|
||||||
RPC_GENERATIONS,
|
|
||||||
)
|
|
||||||
from aioshelly.exceptions import (
|
from aioshelly.exceptions import (
|
||||||
CustomPortNotSupported,
|
CustomPortNotSupported,
|
||||||
DeviceConnectionError,
|
DeviceConnectionError,
|
||||||
@ -461,11 +456,9 @@ class ShellyConfigFlow(ConfigFlow, domain=DOMAIN):
|
|||||||
@callback
|
@callback
|
||||||
def async_supports_options_flow(cls, config_entry: ShellyConfigEntry) -> bool:
|
def async_supports_options_flow(cls, config_entry: ShellyConfigEntry) -> bool:
|
||||||
"""Return options flow support for this handler."""
|
"""Return options flow support for this handler."""
|
||||||
return (
|
return get_device_entry_gen(
|
||||||
get_device_entry_gen(config_entry) in RPC_GENERATIONS
|
config_entry
|
||||||
and not config_entry.data.get(CONF_SLEEP_PERIOD)
|
) in RPC_GENERATIONS and not config_entry.data.get(CONF_SLEEP_PERIOD)
|
||||||
and config_entry.data.get(CONF_MODEL) != MODEL_WALL_DISPLAY
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OptionsFlowHandler(OptionsFlow):
|
class OptionsFlowHandler(OptionsFlow):
|
||||||
@ -475,6 +468,13 @@ class OptionsFlowHandler(OptionsFlow):
|
|||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> ConfigFlowResult:
|
) -> ConfigFlowResult:
|
||||||
"""Handle options flow."""
|
"""Handle options flow."""
|
||||||
|
if (
|
||||||
|
supports_scripts := self.config_entry.runtime_data.rpc_supports_scripts
|
||||||
|
) is None:
|
||||||
|
return self.async_abort(reason="cannot_connect")
|
||||||
|
if not supports_scripts:
|
||||||
|
return self.async_abort(reason="no_scripts_support")
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
return self.async_create_entry(title="", data=user_input)
|
return self.async_create_entry(title="", data=user_input)
|
||||||
|
|
||||||
|
@ -89,6 +89,7 @@ class ShellyEntryData:
|
|||||||
rpc: ShellyRpcCoordinator | None = None
|
rpc: ShellyRpcCoordinator | None = None
|
||||||
rpc_poll: ShellyRpcPollingCoordinator | None = None
|
rpc_poll: ShellyRpcPollingCoordinator | None = None
|
||||||
rpc_script_events: dict[int, list[str]] | None = None
|
rpc_script_events: dict[int, list[str]] | None = None
|
||||||
|
rpc_supports_scripts: bool | None = None
|
||||||
|
|
||||||
|
|
||||||
type ShellyConfigEntry = ConfigEntry[ShellyEntryData]
|
type ShellyConfigEntry = ConfigEntry[ShellyEntryData]
|
||||||
@ -716,6 +717,7 @@ class ShellyRpcCoordinator(ShellyCoordinatorBase[RpcDevice]):
|
|||||||
is updated.
|
is updated.
|
||||||
"""
|
"""
|
||||||
if not self.sleep_period:
|
if not self.sleep_period:
|
||||||
|
if self.config_entry.runtime_data.rpc_supports_scripts:
|
||||||
await self._async_connect_ble_scanner()
|
await self._async_connect_ble_scanner()
|
||||||
else:
|
else:
|
||||||
await self._async_setup_outbound_websocket()
|
await self._async_setup_outbound_websocket()
|
||||||
|
@ -100,6 +100,10 @@
|
|||||||
"ble_scanner_mode": "The scanner mode to use for Bluetooth scanning."
|
"ble_scanner_mode": "The scanner mode to use for Bluetooth scanning."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
|
"no_scripts_support": "Device does not support scripts and cannot be used as a Bluetooth scanner."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"selector": {
|
"selector": {
|
||||||
|
@ -24,6 +24,7 @@ from homeassistant.components.shelly.const import (
|
|||||||
BLEScannerMode,
|
BLEScannerMode,
|
||||||
)
|
)
|
||||||
from homeassistant.components.shelly.coordinator import ENTRY_RELOAD_COOLDOWN
|
from homeassistant.components.shelly.coordinator import ENTRY_RELOAD_COOLDOWN
|
||||||
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_HOST,
|
CONF_HOST,
|
||||||
CONF_MODEL,
|
CONF_MODEL,
|
||||||
@ -744,6 +745,38 @@ async def test_zeroconf_sleeping_device_error(hass: HomeAssistant) -> None:
|
|||||||
assert result["reason"] == "cannot_connect"
|
assert result["reason"] == "cannot_connect"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options_flow_abort_setup_retry(
|
||||||
|
hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
"""Test ble options abort if device is in setup retry."""
|
||||||
|
monkeypatch.setattr(
|
||||||
|
mock_rpc_device, "initialize", AsyncMock(side_effect=DeviceConnectionError)
|
||||||
|
)
|
||||||
|
entry = await init_integration(hass, 2)
|
||||||
|
|
||||||
|
assert entry.state is ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "cannot_connect"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options_flow_abort_no_scripts_support(
|
||||||
|
hass: HomeAssistant, mock_rpc_device: Mock, monkeypatch: pytest.MonkeyPatch
|
||||||
|
) -> None:
|
||||||
|
"""Test ble options abort if device does not support scripts."""
|
||||||
|
monkeypatch.setattr(
|
||||||
|
mock_rpc_device, "supports_scripts", AsyncMock(return_value=False)
|
||||||
|
)
|
||||||
|
entry = await init_integration(hass, 2)
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.ABORT
|
||||||
|
assert result["reason"] == "no_scripts_support"
|
||||||
|
|
||||||
|
|
||||||
async def test_zeroconf_already_configured(hass: HomeAssistant) -> None:
|
async def test_zeroconf_already_configured(hass: HomeAssistant) -> None:
|
||||||
"""Test we get the form."""
|
"""Test we get the form."""
|
||||||
|
|
||||||
|
@ -853,12 +853,17 @@ async def test_rpc_update_entry_fw_ver(
|
|||||||
assert device.sw_version == "99.0.0"
|
assert device.sw_version == "99.0.0"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(("supports_scripts"), [True, False])
|
||||||
async def test_rpc_runs_connected_events_when_initialized(
|
async def test_rpc_runs_connected_events_when_initialized(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_rpc_device: Mock,
|
mock_rpc_device: Mock,
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
|
supports_scripts: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test RPC runs connected events when initialized."""
|
"""Test RPC runs connected events when initialized."""
|
||||||
|
monkeypatch.setattr(
|
||||||
|
mock_rpc_device, "supports_scripts", AsyncMock(return_value=supports_scripts)
|
||||||
|
)
|
||||||
monkeypatch.setattr(mock_rpc_device, "initialized", False)
|
monkeypatch.setattr(mock_rpc_device, "initialized", False)
|
||||||
await init_integration(hass, 2)
|
await init_integration(hass, 2)
|
||||||
|
|
||||||
@ -869,8 +874,9 @@ async def test_rpc_runs_connected_events_when_initialized(
|
|||||||
mock_rpc_device.mock_initialized()
|
mock_rpc_device.mock_initialized()
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# BLE script list is called during connected events
|
assert call.supports_scripts() in mock_rpc_device.mock_calls
|
||||||
assert call.script_list() in mock_rpc_device.mock_calls
|
# BLE script list is called during connected events if device supports scripts
|
||||||
|
assert bool(call.script_list() in mock_rpc_device.mock_calls) == supports_scripts
|
||||||
|
|
||||||
|
|
||||||
async def test_rpc_sleeping_device_unload_ignore_ble_scanner(
|
async def test_rpc_sleeping_device_unload_ignore_ble_scanner(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user