"""Test the shelly bluetooth scanner.""" from unittest.mock import Mock, patch from aioshelly.ble.backend.scanner import ShellyBLEScanner from aioshelly.ble.const import BLE_SCAN_RESULT_EVENT import pytest from homeassistant.components import bluetooth from homeassistant.components.bluetooth import BluetoothScanningMode from homeassistant.components.shelly.const import CONF_BLE_SCANNER_MODE, BLEScannerMode from homeassistant.core import HomeAssistant from .. import init_integration, inject_rpc_device_event async def test_scanner_v1( hass: HomeAssistant, mock_rpc_device, monkeypatch: pytest.MonkeyPatch ) -> None: """Test injecting data into the scanner v1.""" await init_integration( hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} ) assert mock_rpc_device.initialized is True inject_rpc_device_event( monkeypatch, mock_rpc_device, { "events": [ { "component": "script:1", "data": [ 1, "aa:bb:cc:dd:ee:ff", -62, "AgEGCf9ZANH7O3TIkA==", "EQcbxdWlAgC4n+YRTSIADaLLBhYADUgQYQ==", ], "event": BLE_SCAN_RESULT_EVENT, "id": 1, "ts": 1668522399.2, } ], "ts": 1668522399.2, }, ) ble_device = bluetooth.async_ble_device_from_address( hass, "AA:BB:CC:DD:EE:FF", connectable=False ) assert ble_device is not None ble_device = bluetooth.async_ble_device_from_address( hass, "AA:BB:CC:DD:EE:FF", connectable=True ) assert ble_device is None async def test_scanner_v2( hass: HomeAssistant, mock_rpc_device, monkeypatch: pytest.MonkeyPatch ) -> None: """Test injecting data into the scanner v2.""" await init_integration( hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} ) assert mock_rpc_device.initialized is True inject_rpc_device_event( monkeypatch, mock_rpc_device, { "events": [ { "component": "script:1", "data": [ 2, [ [ "aa:bb:cc:dd:ee:ff", -62, "AgEGCf9ZANH7O3TIkA==", "EQcbxdWlAgC4n+YRTSIADaLLBhYADUgQYQ==", ] ], ], "event": BLE_SCAN_RESULT_EVENT, "id": 1, "ts": 1668522399.2, } ], "ts": 1668522399.2, }, ) ble_device = bluetooth.async_ble_device_from_address( hass, "AA:BB:CC:DD:EE:FF", connectable=False ) assert ble_device is not None ble_device = bluetooth.async_ble_device_from_address( hass, "AA:BB:CC:DD:EE:FF", connectable=True ) assert ble_device is None async def test_scanner_ignores_non_ble_events( hass: HomeAssistant, mock_rpc_device, monkeypatch: pytest.MonkeyPatch ) -> None: """Test injecting non ble data into the scanner.""" await init_integration( hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} ) assert mock_rpc_device.initialized is True inject_rpc_device_event( monkeypatch, mock_rpc_device, { "events": [ { "component": "script:1", "data": [], "event": "not_ble_scan_result", "id": 1, "ts": 1668522399.2, } ], "ts": 1668522399.2, }, ) async def test_scanner_ignores_wrong_version_and_logs( hass: HomeAssistant, mock_rpc_device, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ) -> None: """Test injecting wrong version of ble data into the scanner.""" await init_integration( hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} ) assert mock_rpc_device.initialized is True inject_rpc_device_event( monkeypatch, mock_rpc_device, { "events": [ { "component": "script:1", "data": [ 0, "aa:bb:cc:dd:ee:ff", -62, "AgEGCf9ZANH7O3TIkA==", "EQcbxdWlAgC4n+YRTSIADaLLBhYADUgQYQ==", ], "event": BLE_SCAN_RESULT_EVENT, "id": 1, "ts": 1668522399.2, } ], "ts": 1668522399.2, }, ) assert "Unsupported BLE scan result version: 0" in caplog.text async def test_scanner_warns_on_corrupt_event( hass: HomeAssistant, mock_rpc_device, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ) -> None: """Test injecting garbage ble data into the scanner.""" await init_integration( hass, 2, options={CONF_BLE_SCANNER_MODE: BLEScannerMode.ACTIVE} ) assert mock_rpc_device.initialized is True inject_rpc_device_event( monkeypatch, mock_rpc_device, { "events": [ { "component": "script:1", "data": [ 1, ], "event": BLE_SCAN_RESULT_EVENT, "id": 1, "ts": 1668522399.2, } ], "ts": 1668522399.2, }, ) assert "Failed to parse BLE event" in caplog.text async def test_scanner_auto_mode_starts_passive_and_binds_provider( hass: HomeAssistant, mock_rpc_device: Mock ) -> None: """AUTO runs the radio passive; the scanner is pinned AUTO so the worker spawns.""" with patch.object( ShellyBLEScanner, "set_active_window_provider", autospec=True ) as mock_set_provider: await init_integration(hass, 2, options={CONF_BLE_SCANNER_MODE: "auto"}) assert mock_rpc_device.initialized is True scanner = bluetooth.async_scanner_by_source(hass, "12:34:56:78:9A:BE") assert scanner is not None assert scanner.requested_mode is BluetoothScanningMode.AUTO assert scanner.current_mode is BluetoothScanningMode.PASSIVE mock_set_provider.assert_called_once_with(scanner, mock_rpc_device)