mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Fix startup race in BLE integrations (#75780)
This commit is contained in:
parent
157f7292d7
commit
1e85ddabfd
@ -42,17 +42,6 @@ class PassiveBluetoothDataUpdateCoordinator(BasePassiveBluetoothCoordinator):
|
|||||||
super()._async_handle_unavailable(address)
|
super()._async_handle_unavailable(address)
|
||||||
self.async_update_listeners()
|
self.async_update_listeners()
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_start(self) -> CALLBACK_TYPE:
|
|
||||||
"""Start the data updater."""
|
|
||||||
self._async_start()
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def _async_cancel() -> None:
|
|
||||||
self._async_stop()
|
|
||||||
|
|
||||||
return _async_cancel
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_listener(
|
def async_add_listener(
|
||||||
self, update_callback: CALLBACK_TYPE, context: Any = None
|
self, update_callback: CALLBACK_TYPE, context: Any = None
|
||||||
|
@ -78,21 +78,10 @@ class PassiveBluetoothProcessorCoordinator(BasePassiveBluetoothCoordinator):
|
|||||||
def remove_processor() -> None:
|
def remove_processor() -> None:
|
||||||
"""Remove a processor."""
|
"""Remove a processor."""
|
||||||
self._processors.remove(processor)
|
self._processors.remove(processor)
|
||||||
self._async_handle_processors_changed()
|
|
||||||
|
|
||||||
self._processors.append(processor)
|
self._processors.append(processor)
|
||||||
self._async_handle_processors_changed()
|
|
||||||
return remove_processor
|
return remove_processor
|
||||||
|
|
||||||
@callback
|
|
||||||
def _async_handle_processors_changed(self) -> None:
|
|
||||||
"""Handle processors changed."""
|
|
||||||
running = bool(self._cancel_bluetooth_advertisements)
|
|
||||||
if running and not self._processors:
|
|
||||||
self._async_stop()
|
|
||||||
elif not running and self._processors:
|
|
||||||
self._async_start()
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_handle_unavailable(self, address: str) -> None:
|
def _async_handle_unavailable(self, address: str) -> None:
|
||||||
"""Handle the device going unavailable."""
|
"""Handle the device going unavailable."""
|
||||||
|
@ -38,6 +38,17 @@ class BasePassiveBluetoothCoordinator:
|
|||||||
self._present = False
|
self._present = False
|
||||||
self.last_seen = 0.0
|
self.last_seen = 0.0
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_start(self) -> CALLBACK_TYPE:
|
||||||
|
"""Start the data updater."""
|
||||||
|
self._async_start()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def _async_cancel() -> None:
|
||||||
|
self._async_stop()
|
||||||
|
|
||||||
|
return _async_cancel
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def available(self) -> bool:
|
def available(self) -> bool:
|
||||||
"""Return if the device is available."""
|
"""Return if the device is available."""
|
||||||
|
@ -21,7 +21,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
"""Set up Govee BLE device from a config entry."""
|
"""Set up Govee BLE device from a config entry."""
|
||||||
address = entry.unique_id
|
address = entry.unique_id
|
||||||
assert address is not None
|
assert address is not None
|
||||||
hass.data.setdefault(DOMAIN, {})[
|
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||||
entry.entry_id
|
entry.entry_id
|
||||||
] = PassiveBluetoothProcessorCoordinator(
|
] = PassiveBluetoothProcessorCoordinator(
|
||||||
hass,
|
hass,
|
||||||
@ -29,6 +29,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
address=address,
|
address=address,
|
||||||
)
|
)
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
entry.async_on_unload(
|
||||||
|
coordinator.async_start()
|
||||||
|
) # only start after all platforms have had a chance to subscribe
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,12 +135,12 @@ async def async_setup_entry(
|
|||||||
data.update(service_info)
|
data.update(service_info)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
entry.async_on_unload(coordinator.async_register_processor(processor))
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
processor.async_add_entities_listener(
|
processor.async_add_entities_listener(
|
||||||
GoveeBluetoothSensorEntity, async_add_entities
|
GoveeBluetoothSensorEntity, async_add_entities
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
entry.async_on_unload(coordinator.async_register_processor(processor))
|
||||||
|
|
||||||
|
|
||||||
class GoveeBluetoothSensorEntity(
|
class GoveeBluetoothSensorEntity(
|
||||||
|
@ -21,7 +21,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
"""Set up INKBIRD BLE device from a config entry."""
|
"""Set up INKBIRD BLE device from a config entry."""
|
||||||
address = entry.unique_id
|
address = entry.unique_id
|
||||||
assert address is not None
|
assert address is not None
|
||||||
hass.data.setdefault(DOMAIN, {})[
|
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||||
entry.entry_id
|
entry.entry_id
|
||||||
] = PassiveBluetoothProcessorCoordinator(
|
] = PassiveBluetoothProcessorCoordinator(
|
||||||
hass,
|
hass,
|
||||||
@ -29,6 +29,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
address=address,
|
address=address,
|
||||||
)
|
)
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
entry.async_on_unload(
|
||||||
|
coordinator.async_start()
|
||||||
|
) # only start after all platforms have had a chance to subscribe
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -135,12 +135,12 @@ async def async_setup_entry(
|
|||||||
data.update(service_info)
|
data.update(service_info)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
entry.async_on_unload(coordinator.async_register_processor(processor))
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
processor.async_add_entities_listener(
|
processor.async_add_entities_listener(
|
||||||
INKBIRDBluetoothSensorEntity, async_add_entities
|
INKBIRDBluetoothSensorEntity, async_add_entities
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
entry.async_on_unload(coordinator.async_register_processor(processor))
|
||||||
|
|
||||||
|
|
||||||
class INKBIRDBluetoothSensorEntity(
|
class INKBIRDBluetoothSensorEntity(
|
||||||
|
@ -21,7 +21,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
"""Set up Moat BLE device from a config entry."""
|
"""Set up Moat BLE device from a config entry."""
|
||||||
address = entry.unique_id
|
address = entry.unique_id
|
||||||
assert address is not None
|
assert address is not None
|
||||||
hass.data.setdefault(DOMAIN, {})[
|
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||||
entry.entry_id
|
entry.entry_id
|
||||||
] = PassiveBluetoothProcessorCoordinator(
|
] = PassiveBluetoothProcessorCoordinator(
|
||||||
hass,
|
hass,
|
||||||
@ -29,6 +29,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
address=address,
|
address=address,
|
||||||
)
|
)
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
entry.async_on_unload(
|
||||||
|
coordinator.async_start()
|
||||||
|
) # only start after all platforms have had a chance to subscribe
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,12 +142,12 @@ async def async_setup_entry(
|
|||||||
data.update(service_info)
|
data.update(service_info)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
entry.async_on_unload(coordinator.async_register_processor(processor))
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
processor.async_add_entities_listener(
|
processor.async_add_entities_listener(
|
||||||
MoatBluetoothSensorEntity, async_add_entities
|
MoatBluetoothSensorEntity, async_add_entities
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
entry.async_on_unload(coordinator.async_register_processor(processor))
|
||||||
|
|
||||||
|
|
||||||
class MoatBluetoothSensorEntity(
|
class MoatBluetoothSensorEntity(
|
||||||
|
@ -21,7 +21,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
"""Set up SensorPush BLE device from a config entry."""
|
"""Set up SensorPush BLE device from a config entry."""
|
||||||
address = entry.unique_id
|
address = entry.unique_id
|
||||||
assert address is not None
|
assert address is not None
|
||||||
hass.data.setdefault(DOMAIN, {})[
|
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||||
entry.entry_id
|
entry.entry_id
|
||||||
] = PassiveBluetoothProcessorCoordinator(
|
] = PassiveBluetoothProcessorCoordinator(
|
||||||
hass,
|
hass,
|
||||||
@ -29,6 +29,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
address=address,
|
address=address,
|
||||||
)
|
)
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
entry.async_on_unload(
|
||||||
|
coordinator.async_start()
|
||||||
|
) # only start after all platforms have had a chance to subscribe
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,12 +136,12 @@ async def async_setup_entry(
|
|||||||
data.update(service_info)
|
data.update(service_info)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
entry.async_on_unload(coordinator.async_register_processor(processor))
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
processor.async_add_entities_listener(
|
processor.async_add_entities_listener(
|
||||||
SensorPushBluetoothSensorEntity, async_add_entities
|
SensorPushBluetoothSensorEntity, async_add_entities
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
entry.async_on_unload(coordinator.async_register_processor(processor))
|
||||||
|
|
||||||
|
|
||||||
class SensorPushBluetoothSensorEntity(
|
class SensorPushBluetoothSensorEntity(
|
||||||
|
@ -21,7 +21,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
"""Set up Xiaomi BLE device from a config entry."""
|
"""Set up Xiaomi BLE device from a config entry."""
|
||||||
address = entry.unique_id
|
address = entry.unique_id
|
||||||
assert address is not None
|
assert address is not None
|
||||||
hass.data.setdefault(DOMAIN, {})[
|
coordinator = hass.data.setdefault(DOMAIN, {})[
|
||||||
entry.entry_id
|
entry.entry_id
|
||||||
] = PassiveBluetoothProcessorCoordinator(
|
] = PassiveBluetoothProcessorCoordinator(
|
||||||
hass,
|
hass,
|
||||||
@ -29,6 +29,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
address=address,
|
address=address,
|
||||||
)
|
)
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
entry.async_on_unload(
|
||||||
|
coordinator.async_start()
|
||||||
|
) # only start after all platforms have had a chance to subscribe
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -167,12 +167,12 @@ async def async_setup_entry(
|
|||||||
data.update(service_info)
|
data.update(service_info)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
entry.async_on_unload(coordinator.async_register_processor(processor))
|
|
||||||
entry.async_on_unload(
|
entry.async_on_unload(
|
||||||
processor.async_add_entities_listener(
|
processor.async_add_entities_listener(
|
||||||
XiaomiBluetoothSensorEntity, async_add_entities
|
XiaomiBluetoothSensorEntity, async_add_entities
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
entry.async_on_unload(coordinator.async_register_processor(processor))
|
||||||
|
|
||||||
|
|
||||||
class XiaomiBluetoothSensorEntity(
|
class XiaomiBluetoothSensorEntity(
|
||||||
|
@ -107,6 +107,7 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
|||||||
_async_register_callback,
|
_async_register_callback,
|
||||||
):
|
):
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
entity_key = PassiveBluetoothEntityKey("temperature", None)
|
entity_key = PassiveBluetoothEntityKey("temperature", None)
|
||||||
entity_key_events = []
|
entity_key_events = []
|
||||||
@ -171,6 +172,7 @@ async def test_basic_usage(hass, mock_bleak_scanner_start):
|
|||||||
assert coordinator.available is True
|
assert coordinator.available is True
|
||||||
|
|
||||||
unregister_processor()
|
unregister_processor()
|
||||||
|
cancel_coordinator()
|
||||||
|
|
||||||
|
|
||||||
async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
||||||
@ -206,6 +208,7 @@ async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
|||||||
_async_register_callback,
|
_async_register_callback,
|
||||||
):
|
):
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
mock_entity = MagicMock()
|
mock_entity = MagicMock()
|
||||||
mock_add_entities = MagicMock()
|
mock_add_entities = MagicMock()
|
||||||
@ -259,6 +262,7 @@ async def test_unavailable_after_no_data(hass, mock_bleak_scanner_start):
|
|||||||
assert processor.available is False
|
assert processor.available is False
|
||||||
|
|
||||||
unregister_processor()
|
unregister_processor()
|
||||||
|
cancel_coordinator()
|
||||||
|
|
||||||
|
|
||||||
async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
||||||
@ -290,6 +294,7 @@ async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
|||||||
_async_register_callback,
|
_async_register_callback,
|
||||||
):
|
):
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
all_events = []
|
all_events = []
|
||||||
|
|
||||||
@ -310,6 +315,7 @@ async def test_no_updates_once_stopping(hass, mock_bleak_scanner_start):
|
|||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
||||||
assert len(all_events) == 1
|
assert len(all_events) == 1
|
||||||
unregister_processor()
|
unregister_processor()
|
||||||
|
cancel_coordinator()
|
||||||
|
|
||||||
|
|
||||||
async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_start):
|
async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_start):
|
||||||
@ -346,6 +352,7 @@ async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_sta
|
|||||||
_async_register_callback,
|
_async_register_callback,
|
||||||
):
|
):
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
processor.async_add_listener(MagicMock())
|
processor.async_add_listener(MagicMock())
|
||||||
|
|
||||||
@ -361,6 +368,7 @@ async def test_exception_from_update_method(hass, caplog, mock_bleak_scanner_sta
|
|||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
||||||
assert processor.available is True
|
assert processor.available is True
|
||||||
unregister_processor()
|
unregister_processor()
|
||||||
|
cancel_coordinator()
|
||||||
|
|
||||||
|
|
||||||
async def test_bad_data_from_update_method(hass, mock_bleak_scanner_start):
|
async def test_bad_data_from_update_method(hass, mock_bleak_scanner_start):
|
||||||
@ -397,6 +405,7 @@ async def test_bad_data_from_update_method(hass, mock_bleak_scanner_start):
|
|||||||
_async_register_callback,
|
_async_register_callback,
|
||||||
):
|
):
|
||||||
unregister_processor = coordinator.async_register_processor(processor)
|
unregister_processor = coordinator.async_register_processor(processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
processor.async_add_listener(MagicMock())
|
processor.async_add_listener(MagicMock())
|
||||||
|
|
||||||
@ -413,6 +422,7 @@ async def test_bad_data_from_update_method(hass, mock_bleak_scanner_start):
|
|||||||
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
saved_callback(GENERIC_BLUETOOTH_SERVICE_INFO, BluetoothChange.ADVERTISEMENT)
|
||||||
assert processor.available is True
|
assert processor.available is True
|
||||||
unregister_processor()
|
unregister_processor()
|
||||||
|
cancel_coordinator()
|
||||||
|
|
||||||
|
|
||||||
GOVEE_B5178_REMOTE_SERVICE_INFO = BluetoothServiceInfo(
|
GOVEE_B5178_REMOTE_SERVICE_INFO = BluetoothServiceInfo(
|
||||||
@ -737,6 +747,7 @@ async def test_integration_with_entity(hass, mock_bleak_scanner_start):
|
|||||||
_async_register_callback,
|
_async_register_callback,
|
||||||
):
|
):
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
processor.async_add_listener(MagicMock())
|
processor.async_add_listener(MagicMock())
|
||||||
|
|
||||||
@ -781,6 +792,7 @@ async def test_integration_with_entity(hass, mock_bleak_scanner_start):
|
|||||||
assert entity_one.entity_key == PassiveBluetoothEntityKey(
|
assert entity_one.entity_key == PassiveBluetoothEntityKey(
|
||||||
key="temperature", device_id="remote"
|
key="temperature", device_id="remote"
|
||||||
)
|
)
|
||||||
|
cancel_coordinator()
|
||||||
|
|
||||||
|
|
||||||
NO_DEVICES_BLUETOOTH_SERVICE_INFO = BluetoothServiceInfo(
|
NO_DEVICES_BLUETOOTH_SERVICE_INFO = BluetoothServiceInfo(
|
||||||
@ -845,6 +857,7 @@ async def test_integration_with_entity_without_a_device(hass, mock_bleak_scanner
|
|||||||
_async_register_callback,
|
_async_register_callback,
|
||||||
):
|
):
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
mock_add_entities = MagicMock()
|
mock_add_entities = MagicMock()
|
||||||
|
|
||||||
@ -873,6 +886,7 @@ async def test_integration_with_entity_without_a_device(hass, mock_bleak_scanner
|
|||||||
assert entity_one.entity_key == PassiveBluetoothEntityKey(
|
assert entity_one.entity_key == PassiveBluetoothEntityKey(
|
||||||
key="temperature", device_id=None
|
key="temperature", device_id=None
|
||||||
)
|
)
|
||||||
|
cancel_coordinator()
|
||||||
|
|
||||||
|
|
||||||
async def test_passive_bluetooth_entity_with_entity_platform(
|
async def test_passive_bluetooth_entity_with_entity_platform(
|
||||||
@ -907,6 +921,7 @@ async def test_passive_bluetooth_entity_with_entity_platform(
|
|||||||
_async_register_callback,
|
_async_register_callback,
|
||||||
):
|
):
|
||||||
coordinator.async_register_processor(processor)
|
coordinator.async_register_processor(processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
processor.async_add_entities_listener(
|
processor.async_add_entities_listener(
|
||||||
PassiveBluetoothProcessorEntity,
|
PassiveBluetoothProcessorEntity,
|
||||||
@ -926,6 +941,7 @@ async def test_passive_bluetooth_entity_with_entity_platform(
|
|||||||
hass.states.get("test_domain.test_platform_aa_bb_cc_dd_ee_ff_pressure")
|
hass.states.get("test_domain.test_platform_aa_bb_cc_dd_ee_ff_pressure")
|
||||||
is not None
|
is not None
|
||||||
)
|
)
|
||||||
|
cancel_coordinator()
|
||||||
|
|
||||||
|
|
||||||
SENSOR_PASSIVE_BLUETOOTH_DATA_UPDATE = PassiveBluetoothDataUpdate(
|
SENSOR_PASSIVE_BLUETOOTH_DATA_UPDATE = PassiveBluetoothDataUpdate(
|
||||||
@ -999,6 +1015,7 @@ async def test_integration_multiple_entity_platforms(hass, mock_bleak_scanner_st
|
|||||||
):
|
):
|
||||||
coordinator.async_register_processor(binary_sensor_processor)
|
coordinator.async_register_processor(binary_sensor_processor)
|
||||||
coordinator.async_register_processor(sesnor_processor)
|
coordinator.async_register_processor(sesnor_processor)
|
||||||
|
cancel_coordinator = coordinator.async_start()
|
||||||
|
|
||||||
binary_sensor_processor.async_add_listener(MagicMock())
|
binary_sensor_processor.async_add_listener(MagicMock())
|
||||||
sesnor_processor.async_add_listener(MagicMock())
|
sesnor_processor.async_add_listener(MagicMock())
|
||||||
@ -1056,3 +1073,4 @@ async def test_integration_multiple_entity_platforms(hass, mock_bleak_scanner_st
|
|||||||
assert binary_sensor_entity_one.entity_key == PassiveBluetoothEntityKey(
|
assert binary_sensor_entity_one.entity_key == PassiveBluetoothEntityKey(
|
||||||
key="motion", device_id=None
|
key="motion", device_id=None
|
||||||
)
|
)
|
||||||
|
cancel_coordinator()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user