1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Add battery support to Bang & Olufsen (#159994)

This commit is contained in:
Markus Jacobsen
2026-01-07 23:40:22 +01:00
committed by GitHub
parent 2c7852f94b
commit a7440e3756
16 changed files with 459 additions and 9 deletions

View File

@@ -34,7 +34,7 @@ class BeoData:
type BeoConfigEntry = ConfigEntry[BeoData]
PLATFORMS = [Platform.EVENT, Platform.MEDIA_PLAYER]
PLATFORMS = [Platform.EVENT, Platform.MEDIA_PLAYER, Platform.SENSOR]
async def async_setup_entry(hass: HomeAssistant, entry: BeoConfigEntry) -> bool:

View File

@@ -115,6 +115,7 @@ class WebsocketNotification(StrEnum):
"""Enum for WebSocket notification types."""
ACTIVE_LISTENING_MODE = "active_listening_mode"
BATTERY = "battery"
BEO_REMOTE_BUTTON = "beo_remote_button"
BUTTON = "button"
PLAYBACK_ERROR = "playback_error"

View File

@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any
from homeassistant.components.event import DOMAIN as EVENT_DOMAIN
from homeassistant.components.media_player import DOMAIN as MEDIA_PLAYER_DOMAIN
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.const import CONF_MODEL
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
@@ -55,6 +56,19 @@ async def async_get_config_entry_diagnostics(
# Get remotes
for remote in await get_remotes(config_entry.runtime_data.client):
# Get Battery Sensor states
if entity_id := entity_registry.async_get_entity_id(
SENSOR_DOMAIN,
DOMAIN,
f"{remote.serial_number}_{config_entry.unique_id}_remote_battery_level",
):
if state := hass.states.get(entity_id):
state_dict = dict(state.as_dict())
# Remove context as it is not relevant
state_dict.pop("context")
data[f"remote_{remote.serial_number}_battery_level"] = state_dict
# Get key Event entity states (if enabled)
for key_type in get_remote_keys():
if entity_id := entity_registry.async_get_entity_id(
@@ -72,4 +86,15 @@ async def async_get_config_entry_diagnostics(
# Add remote Mozart model
data[f"remote_{remote.serial_number}"] = dict(remote)
# Get Mozart battery entity
if entity_id := entity_registry.async_get_entity_id(
SENSOR_DOMAIN, DOMAIN, f"{config_entry.unique_id}_battery_level"
):
if state := hass.states.get(entity_id):
state_dict = dict(state.as_dict())
# Remove context as it is not relevant
state_dict.pop("context")
data["battery_level"] = state_dict
return data

View File

@@ -0,0 +1,139 @@
"""Sensor entities for the Bang & Olufsen integration."""
from __future__ import annotations
import contextlib
from datetime import timedelta
from aiohttp import ClientConnectorError
from mozart_api.exceptions import ApiException
from mozart_api.models import BatteryState, PairedRemote
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorStateClass,
)
from homeassistant.const import PERCENTAGE
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from . import BeoConfigEntry
from .const import CONNECTION_STATUS, DOMAIN, WebsocketNotification
from .entity import BeoEntity
from .util import get_remotes, supports_battery
SCAN_INTERVAL = timedelta(minutes=15)
PARALLEL_UPDATES = 0
async def async_setup_entry(
hass: HomeAssistant,
config_entry: BeoConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Sensor entities from config entry."""
entities: list[BeoSensor] = []
# Check for Mozart device with battery
if await supports_battery(config_entry.runtime_data.client):
entities.append(BeoSensorBatteryLevel(config_entry))
# Add any Beoremote One remotes
entities.extend(
[
BeoSensorRemoteBatteryLevel(config_entry, remote)
for remote in (await get_remotes(config_entry.runtime_data.client))
]
)
async_add_entities(entities, update_before_add=True)
class BeoSensor(SensorEntity, BeoEntity):
"""Base Bang & Olufsen Sensor."""
def __init__(self, config_entry: BeoConfigEntry) -> None:
"""Initialize Sensor."""
super().__init__(config_entry, config_entry.runtime_data.client)
class BeoSensorBatteryLevel(BeoSensor):
"""Battery level Sensor for Mozart devices."""
_attr_device_class = SensorDeviceClass.BATTERY
_attr_native_unit_of_measurement = PERCENTAGE
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, config_entry: BeoConfigEntry) -> None:
"""Init the battery level Sensor."""
super().__init__(config_entry)
self._attr_unique_id = f"{self._unique_id}_battery_level"
async def async_added_to_hass(self) -> None:
"""Turn on the dispatchers."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{DOMAIN}_{self._unique_id}_{CONNECTION_STATUS}",
self._async_update_connection_state,
)
)
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{DOMAIN}_{self._unique_id}_{WebsocketNotification.BATTERY}",
self._update_battery,
)
)
async def _update_battery(self, data: BatteryState) -> None:
"""Update sensor value."""
self._attr_native_value = data.battery_level
self.async_write_ha_state()
class BeoSensorRemoteBatteryLevel(BeoSensor):
"""Battery level Sensor for the Beoremote One."""
_attr_device_class = SensorDeviceClass.BATTERY
_attr_native_unit_of_measurement = PERCENTAGE
_attr_should_poll = True
_attr_state_class = SensorStateClass.MEASUREMENT
def __init__(self, config_entry: BeoConfigEntry, remote: PairedRemote) -> None:
"""Init the battery level Sensor."""
super().__init__(config_entry)
# Serial number is not None, as the remote object is provided by get_remotes
assert remote.serial_number
self._attr_unique_id = (
f"{remote.serial_number}_{self._unique_id}_remote_battery_level"
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, f"{remote.serial_number}_{self._unique_id}")}
)
self._attr_native_value = remote.battery_level
self._remote = remote
async def async_added_to_hass(self) -> None:
"""Turn on the dispatchers."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{DOMAIN}_{self._unique_id}_{CONNECTION_STATUS}",
self._async_update_connection_state,
)
)
async def async_update(self) -> None:
"""Poll battery status."""
with contextlib.suppress(ApiException, ClientConnectorError, TimeoutError):
for remote in await get_remotes(self._client):
if remote.serial_number == self._remote.serial_number:
self._attr_native_value = remote.battery_level
break

View File

@@ -84,3 +84,10 @@ def get_remote_keys() -> list[str]:
for key_type in (*BEO_REMOTE_KEYS, *BEO_REMOTE_CONTROL_KEYS)
],
]
async def supports_battery(client: MozartClient) -> bool:
"""Get if a Mozart device has a battery."""
battery_state = await client.get_battery_state()
return battery_state.state != "BatteryNotPresent"

View File

@@ -6,6 +6,7 @@ import logging
from typing import TYPE_CHECKING
from mozart_api.models import (
BatteryState,
BeoRemoteButton,
ButtonEvent,
ListeningModeProps,
@@ -60,6 +61,7 @@ class BeoWebsocket(BeoBase):
self._client.get_active_listening_mode_notifications(
self.on_active_listening_mode
)
self._client.get_battery_notifications(self.on_battery_notification)
self._client.get_beo_remote_button_notifications(
self.on_beo_remote_button_notification
)
@@ -115,6 +117,14 @@ class BeoWebsocket(BeoBase):
notification,
)
def on_battery_notification(self, notification: BatteryState) -> None:
"""Send battery dispatch."""
async_dispatcher_send(
self.hass,
f"{DOMAIN}_{self._unique_id}_{WebsocketNotification.BATTERY}",
notification,
)
def on_beo_remote_button_notification(self, notification: BeoRemoteButton) -> None:
"""Send beo_remote_button dispatch."""
if TYPE_CHECKING:

View File

@@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, Mock, patch
from mozart_api.models import (
Action,
BatteryState,
BeolinkPeer,
BeolinkSelf,
ContentItem,
@@ -34,6 +35,7 @@ from homeassistant.components.bang_olufsen.const import DOMAIN
from homeassistant.core import HomeAssistant
from .const import (
TEST_BATTERY,
TEST_DATA_CREATE_ENTRY,
TEST_DATA_CREATE_ENTRY_2,
TEST_DATA_CREATE_ENTRY_3,
@@ -125,6 +127,7 @@ async def mock_websocket_connection(
playback_metadata_callback = (
mock_mozart_client.get_playback_metadata_notifications.call_args[0][0]
)
battery_callback = mock_mozart_client.get_battery_notifications.call_args[0][0]
# Trigger callbacks. Try to use existing data
volume_callback(mock_mozart_client.get_product_state.return_value.volume)
@@ -137,6 +140,10 @@ async def mock_websocket_connection(
playback_metadata_callback(
mock_mozart_client.get_product_state.return_value.playback.metadata
)
# This should not affect non-battery devices.
battery_callback(TEST_BATTERY)
await hass.async_block_till_done()
@@ -403,6 +410,14 @@ def mock_mozart_client() -> Generator[AsyncMock]:
)
]
)
client.get_battery_state = AsyncMock()
client.get_battery_state.return_value = BatteryState(
battery_level=0,
is_charging=False,
remaining_charging_time_minutes=0,
remaining_playing_time_minutes=0,
state="BatteryNotPresent",
)
client.post_standby = AsyncMock()
client.set_current_volume_level = AsyncMock()
client.set_volume_mute = AsyncMock()

View File

@@ -6,6 +6,7 @@ from unittest.mock import Mock
from mozart_api.exceptions import ApiException
from mozart_api.models import (
Action,
BatteryState,
ListeningModeRef,
OverlayPlayRequest,
OverlayPlayRequestTextToSpeechTextToSpeech,
@@ -71,14 +72,18 @@ TEST_NAME_4 = f"{TEST_MODEL_A5}-{TEST_SERIAL_NUMBER_4}"
TEST_JID_4 = f"{TEST_TYPE_NUMBER}.{TEST_ITEM_NUMBER}.{TEST_SERIAL_NUMBER_4}@products.bang-olufsen.com"
TEST_MEDIA_PLAYER_ENTITY_ID_4 = f"media_player.beosound_a5_{TEST_SERIAL_NUMBER_4}"
TEST_HOST_4 = "192.168.0.4"
TEST_BATTERY_A5_SENSOR_ENTITY_ID = f"sensor.beosound_a5_{TEST_SERIAL_NUMBER_4}_battery"
# Beoremote One
TEST_REMOTE_SERIAL = "55555555"
TEST_REMOTE_SERIAL_PAIRED = f"{TEST_REMOTE_SERIAL}_{TEST_SERIAL_NUMBER}"
TEST_REMOTE_SW_VERSION = "1.0.0"
TEST_BUTTON_EVENT_ENTITY_ID = "event.beosound_balance_11111111_play_pause"
TEST_REMOTE_KEY_EVENT_ENTITY_ID = "event.beoremote_one_55555555_11111111_control_play"
TEST_REMOTE_BATTERY_LEVEL_SENSOR_ENTITY_ID = (
"sensor.beoremote_one_55555555_11111111_battery"
)
TEST_BUTTON_EVENT_ENTITY_ID = "event.beosound_balance_11111111_play_pause"
TEST_HOSTNAME_ZEROCONF = TEST_NAME.replace(" ", "-") + ".local."
TEST_TYPE_ZEROCONF = "_bangolufsen._tcp.local."
@@ -255,3 +260,10 @@ TEST_SOUND_MODES = [
TEST_ACTIVE_SOUND_MODE_NAME_2,
f"{TEST_SOUND_MODE_NAME} 2 (345)",
]
TEST_BATTERY = BatteryState(
battery_level=5,
is_charging=False,
remaining_charging_time_minutes=0,
remaining_playing_time_minutes=0,
state="BatteryVeryLow",
)

View File

@@ -106,6 +106,117 @@
'entity_id': 'event.beoremote_one_55555555_11111111_control_play',
'state': 'unknown',
}),
'remote_55555555_battery_level': dict({
'attributes': dict({
'device_class': 'battery',
'friendly_name': 'Beoremote One-55555555-11111111 Battery',
'state_class': 'measurement',
'unit_of_measurement': '%',
}),
'entity_id': 'sensor.beoremote_one_55555555_11111111_battery',
'state': '50',
}),
'websocket_connected': False,
})
# ---
# name: test_async_get_config_entry_diagnostics_with_battery
dict({
'battery_level': dict({
'attributes': dict({
'device_class': 'battery',
'friendly_name': 'Living room Balance Battery',
'state_class': 'measurement',
'unit_of_measurement': '%',
}),
'entity_id': 'sensor.beosound_a5_44444444_battery',
'state': '5',
}),
'config_entry': dict({
'data': dict({
'host': '192.168.0.4',
'jid': '1111.1111111.44444444@products.bang-olufsen.com',
'model': 'Beosound A5',
'name': 'Beosound A5-44444444',
}),
'disabled_by': None,
'discovery_keys': dict({
}),
'domain': 'bang_olufsen',
'minor_version': 1,
'options': dict({
}),
'pref_disable_new_entities': False,
'pref_disable_polling': False,
'source': 'user',
'subentries': list([
]),
'title': 'Beosound A5-44444444',
'unique_id': '44444444',
'version': 1,
}),
'media_player': dict({
'attributes': dict({
'beolink': dict({
'listeners': dict({
'Bedroom Premiere': '1111.1111111.33333333@products.bang-olufsen.com',
'Lounge room A5': '1111.1111111.44444444@products.bang-olufsen.com',
}),
'peers': dict({
'Bedroom Premiere': '1111.1111111.33333333@products.bang-olufsen.com',
'Lounge room A5': '1111.1111111.44444444@products.bang-olufsen.com',
}),
'self': dict({
'Living room Balance': '1111.1111111.44444444@products.bang-olufsen.com',
}),
}),
'device_class': 'speaker',
'entity_picture_local': None,
'friendly_name': 'Living room Balance',
'group_members': list([
'media_player.beosound_a5_44444444',
'listener_not_in_hass-1111.1111111.33333333@products.bang-olufsen.com',
'media_player.beosound_a5_44444444',
]),
'media_content_type': 'music',
'repeat': 'off',
'shuffle': False,
'sound_mode': 'Test Listening Mode (123)',
'sound_mode_list': list([
'Test Listening Mode (123)',
'Test Listening Mode (234)',
'Test Listening Mode 2 (345)',
]),
'source_list': list([
'Tidal',
'Line-In',
'HDMI A',
]),
'supported_features': 2095933,
}),
'entity_id': 'media_player.beosound_a5_44444444',
'state': 'playing',
}),
'remote_55555555': dict({
'address': '',
'app_version': '1.0.0',
'battery_level': 50,
'connected': True,
'db_version': None,
'last_seen': None,
'name': 'BEORC',
'serial_number': '55555555',
'updated': None,
}),
'remote_55555555_battery_level': dict({
'attributes': dict({
'device_class': 'battery',
'friendly_name': 'Beoremote One-55555555-44444444 Battery',
'state_class': 'measurement',
'unit_of_measurement': '%',
}),
'entity_id': 'sensor.beoremote_one_55555555_44444444_battery',
'state': '50',
}),
'websocket_connected': False,
})
# ---

View File

@@ -100,6 +100,8 @@
'event.beoremote_one_55555555_44444444_control_function_25',
'event.beoremote_one_55555555_44444444_control_function_26',
'event.beoremote_one_55555555_44444444_control_function_27',
'sensor.beosound_a5_44444444_battery',
'sensor.beoremote_one_55555555_44444444_battery',
'media_player.beosound_a5_44444444',
])
# ---
@@ -205,6 +207,7 @@
'event.beoremote_one_55555555_11111111_control_function_25',
'event.beoremote_one_55555555_11111111_control_function_26',
'event.beoremote_one_55555555_11111111_control_function_27',
'sensor.beoremote_one_55555555_11111111_battery',
'media_player.beosound_balance_11111111',
])
# ---
@@ -308,6 +311,7 @@
'event.beoremote_one_55555555_33333333_control_function_25',
'event.beoremote_one_55555555_33333333_control_function_26',
'event.beoremote_one_55555555_33333333_control_function_27',
'sensor.beoremote_one_55555555_33333333_battery',
'media_player.beosound_premiere_33333333',
])
# ---

View File

@@ -101,6 +101,7 @@
'event.beoremote_one_55555555_11111111_control_function_25',
'event.beoremote_one_55555555_11111111_control_function_26',
'event.beoremote_one_55555555_11111111_control_function_27',
'sensor.beoremote_one_55555555_11111111_battery',
'media_player.beosound_balance_11111111',
])
# ---
@@ -206,6 +207,7 @@
'event.beoremote_one_55555555_11111111_control_function_25',
'event.beoremote_one_55555555_11111111_control_function_26',
'event.beoremote_one_55555555_11111111_control_function_27',
'sensor.beoremote_one_55555555_11111111_battery',
'media_player.beosound_balance_11111111',
'event.beoremote_one_66666666_11111111_light_blue',
'event.beoremote_one_66666666_11111111_light_digit_0',
@@ -297,6 +299,7 @@
'event.beoremote_one_66666666_11111111_control_function_25',
'event.beoremote_one_66666666_11111111_control_function_26',
'event.beoremote_one_66666666_11111111_control_function_27',
'sensor.beoremote_one_66666666_11111111_battery',
])
# ---
# name: test_on_remote_control_unpaired

View File

@@ -1,5 +1,6 @@
"""Test bang_olufsen config entry diagnostics."""
from mozart_api.models import BatteryState
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
@@ -51,3 +52,39 @@ async def test_async_get_config_entry_diagnostics(
"modified_at",
)
)
async def test_async_get_config_entry_diagnostics_with_battery(
hass: HomeAssistant,
entity_registry: EntityRegistry,
hass_client: ClientSessionGenerator,
mock_config_entry_a5: MockConfigEntry,
mock_mozart_client: AsyncMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test config entry diagnostics for devices with a battery."""
mock_mozart_client.get_battery_state.return_value = BatteryState(
battery_level=1, state="BatteryVeryLow"
)
# Load entry
mock_config_entry_a5.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry_a5.entry_id)
await mock_websocket_connection(hass, mock_mozart_client)
result = await get_diagnostics_for_config_entry(
hass, hass_client, mock_config_entry_a5
)
assert result == snapshot(
exclude=props(
"created_at",
"entry_id",
"id",
"last_changed",
"last_reported",
"last_updated",
"media_position_updated_at",
"modified_at",
)
)

View File

@@ -18,6 +18,7 @@ from homeassistant.helpers.entity_registry import EntityRegistry
from .conftest import mock_websocket_connection
from .const import (
TEST_BATTERY,
TEST_BUTTON_EVENT_ENTITY_ID,
TEST_REMOTE_KEY_EVENT_ENTITY_ID,
TEST_SERIAL_NUMBER_3,
@@ -130,6 +131,7 @@ async def test_button_event_creation_a5(
snapshot: SnapshotAssertion,
) -> None:
"""Test Microphone button event entity is not created when using a Beosound A5."""
mock_mozart_client.get_battery_state.return_value = TEST_BATTERY
await _check_button_event_creation(
hass,

View File

@@ -0,0 +1,80 @@
"""Test the bang_olufsen sensor entities."""
from unittest.mock import AsyncMock
from freezegun.api import FrozenDateTimeFactory
from mozart_api.models import PairedRemote, PairedRemoteResponse
from homeassistant.components.bang_olufsen.sensor import SCAN_INTERVAL
from homeassistant.const import STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from .conftest import mock_websocket_connection
from .const import (
TEST_BATTERY,
TEST_BATTERY_A5_SENSOR_ENTITY_ID,
TEST_REMOTE_BATTERY_LEVEL_SENSOR_ENTITY_ID,
TEST_REMOTE_SERIAL,
)
from tests.common import MockConfigEntry, async_fire_time_changed
async def test_battery_level(
hass: HomeAssistant,
mock_mozart_client: AsyncMock,
mock_config_entry_a5: MockConfigEntry,
) -> None:
"""Test the battery level entity."""
# Ensure battery entities are created
mock_mozart_client.get_battery_state.return_value = TEST_BATTERY
# Load entry
mock_config_entry_a5.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry_a5.entry_id)
# Deliberately avoid triggering a battery notification
assert (states := hass.states.get(TEST_BATTERY_A5_SENSOR_ENTITY_ID))
assert states.state is STATE_UNKNOWN
# Check sensor reacts as expected to WebSocket events
await mock_websocket_connection(hass, mock_mozart_client)
assert (states := hass.states.get(TEST_BATTERY_A5_SENSOR_ENTITY_ID))
assert states.state == str(TEST_BATTERY.battery_level)
async def test_remote_battery_level(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
integration: None,
mock_config_entry: MockConfigEntry,
mock_mozart_client: AsyncMock,
) -> None:
"""Test the remote battery level entity."""
# Check the default value is set
assert (states := hass.states.get(TEST_REMOTE_BATTERY_LEVEL_SENSOR_ENTITY_ID))
assert states.state == "50"
# Change battery level
mock_mozart_client.get_bluetooth_remotes.return_value = PairedRemoteResponse(
items=[
PairedRemote(
address="",
app_version="1.0.0",
battery_level=45,
connected=True,
serial_number=TEST_REMOTE_SERIAL,
name="BEORC",
)
]
)
# Trigger poll update
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert (states := hass.states.get(TEST_REMOTE_BATTERY_LEVEL_SENSOR_ENTITY_ID))
assert states.state == "45"

View File

@@ -130,7 +130,7 @@ async def test_on_remote_control_already_added(
await hass.config_entries.async_setup(mock_config_entry.entry_id)
# Check device and API call count
assert mock_mozart_client.get_bluetooth_remotes.call_count == 1
assert mock_mozart_client.get_bluetooth_remotes.call_count == 3
assert device_registry.async_get_device({(DOMAIN, TEST_REMOTE_SERIAL_PAIRED)})
# Check number of entities (remote and button events and media_player)
@@ -149,7 +149,7 @@ async def test_on_remote_control_already_added(
await hass.async_block_till_done()
# Check device and API call count (triggered once by the WebSocket notification)
assert mock_mozart_client.get_bluetooth_remotes.call_count == 2
assert mock_mozart_client.get_bluetooth_remotes.call_count == 4
assert device_registry.async_get_device({(DOMAIN, TEST_REMOTE_SERIAL_PAIRED)})
# Check number of entities (remote and button events and media_player)
@@ -176,7 +176,7 @@ async def test_on_remote_control_paired(
await hass.config_entries.async_setup(mock_config_entry.entry_id)
# Check device and API call count
assert mock_mozart_client.get_bluetooth_remotes.call_count == 1
assert mock_mozart_client.get_bluetooth_remotes.call_count == 3
assert device_registry.async_get_device({(DOMAIN, TEST_REMOTE_SERIAL_PAIRED)})
# Check number of entities (button and remote events and media_player)
@@ -217,7 +217,7 @@ async def test_on_remote_control_paired(
await hass.async_block_till_done()
# Check device and API call count
assert mock_mozart_client.get_bluetooth_remotes.call_count == 3
assert mock_mozart_client.get_bluetooth_remotes.call_count == 8
assert device_registry.async_get_device({(DOMAIN, TEST_REMOTE_SERIAL_PAIRED)})
assert device_registry.async_get_device(
{(DOMAIN, f"66666666_{TEST_SERIAL_NUMBER}")}
@@ -257,7 +257,7 @@ async def test_on_remote_control_unpaired(
await hass.config_entries.async_setup(mock_config_entry.entry_id)
# Check device and API call count
assert mock_mozart_client.get_bluetooth_remotes.call_count == 1
assert mock_mozart_client.get_bluetooth_remotes.call_count == 3
assert device_registry.async_get_device({(DOMAIN, TEST_REMOTE_SERIAL_PAIRED)})
# Check number of entities (button and remote events and media_player)
@@ -280,7 +280,7 @@ async def test_on_remote_control_unpaired(
await hass.async_block_till_done()
# Check device and API call count
assert mock_mozart_client.get_bluetooth_remotes.call_count == 3
assert mock_mozart_client.get_bluetooth_remotes.call_count == 6
assert (
device_registry.async_get_device({(DOMAIN, TEST_REMOTE_SERIAL_PAIRED)}) is None
)

View File

@@ -11,6 +11,7 @@ from homeassistant.components.bang_olufsen.const import (
)
from .const import (
TEST_BATTERY_A5_SENSOR_ENTITY_ID,
TEST_MEDIA_PLAYER_ENTITY_ID,
TEST_MEDIA_PLAYER_ENTITY_ID_2,
TEST_MEDIA_PLAYER_ENTITY_ID_3,
@@ -51,6 +52,7 @@ def get_a5_entity_ids() -> list[str]:
"""Return a list of entity_ids that a Beosound A5 provides."""
buttons = [
TEST_MEDIA_PLAYER_ENTITY_ID_4,
TEST_BATTERY_A5_SENSOR_ENTITY_ID,
*_get_button_entity_ids("beosound_a5_44444444"),
]
buttons.remove("event.beosound_a5_44444444_microphone")
@@ -66,7 +68,9 @@ def get_remote_entity_ids(
remote_serial: str = TEST_REMOTE_SERIAL, device_serial: str = TEST_SERIAL_NUMBER
) -> list[str]:
"""Return a list of entity_ids that the Beoremote One provides."""
entity_ids: list[str] = []
entity_ids: list[str] = [
f"sensor.beoremote_one_{remote_serial}_{device_serial}_battery"
]
# Add remote light key Event entity ids
entity_ids.extend(