Skip to content

Commit

Permalink
Store BangOlufsenData in config_entry runtime data
Browse files Browse the repository at this point in the history
Standardize config_entry naming from entry to config_entry
Remove client as init parameter for all entity classes
Toggle seek support flag instead of raising an exception when the source is not seekable
  • Loading branch information
mj23000 committed Oct 28, 2024
1 parent 6689d10 commit ae1b8f3
Show file tree
Hide file tree
Showing 17 changed files with 249 additions and 228 deletions.
63 changes: 31 additions & 32 deletions custom_components/bang_olufsen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from mozart_api.exceptions import ApiException
from mozart_api.mozart_client import MozartClient

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_MODEL, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
Expand All @@ -17,7 +16,7 @@

from .const import DOMAIN
from .coordinator import BangOlufsenCoordinator
from .util import BangOlufsenData, get_remote
from .util import BangOlufsenConfigEntry, BangOlufsenData, get_remote

PLATFORMS = [
Platform.BINARY_SENSOR,
Expand Down Expand Up @@ -45,23 +44,27 @@ async def _start_websocket_listener(data: BangOlufsenData) -> None:
await data.client.connect_notifications(remote_control=True, reconnect=True)


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_setup_entry(
hass: HomeAssistant, config_entry: BangOlufsenConfigEntry
) -> bool:
"""Set up from a config entry."""

# Remove casts to str
assert entry.unique_id
assert config_entry.unique_id

# Create device now as BangOlufsenWebsocket needs a device for debug logging, firing events etc.
# And in order to ensure entity platforms (button, binary_sensor) have device name before the primary (media_player) is initialized
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
identifiers={(DOMAIN, entry.unique_id)},
name=entry.title,
model=entry.data[CONF_MODEL],
config_entry_id=config_entry.entry_id,
identifiers={(DOMAIN, config_entry.unique_id)},
name=config_entry.title,
model=config_entry.data[CONF_MODEL],
)

client = MozartClient(host=entry.data[CONF_HOST], ssl_context=get_default_context())
client = MozartClient(
host=config_entry.data[CONF_HOST], ssl_context=get_default_context()
)

# Check API and WebSocket connection
try:
Expand All @@ -74,34 +77,33 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
TimeoutError,
) as error:
await client.close_api_client()
raise ConfigEntryNotReady(f"Unable to connect to {entry.title}") from error
raise ConfigEntryNotReady(
f"Unable to connect to {config_entry.title}"
) from error

# Initialize coordinator
coordinator = BangOlufsenCoordinator(hass, entry, client)
coordinator = BangOlufsenCoordinator(hass, config_entry, client)
await coordinator.async_config_entry_first_refresh()

# Add the coordinator and API client
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = BangOlufsenData(
coordinator,
client,
)
config_entry.runtime_data = BangOlufsenData(coordinator, client)

# Check for connected Beoremote One
if remote := await get_remote(client):
assert remote.serial_number

# Create Beoremote One device
assert entry.unique_id
assert config_entry.unique_id
device_registry = dr.async_get(hass)
device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
config_entry_id=config_entry.entry_id,
identifiers={(DOMAIN, remote.serial_number)},
name=f"Beoremote One {remote.serial_number}",
model="Beoremote One",
serial_number=remote.serial_number,
sw_version=remote.app_version,
manufacturer="Bang & Olufsen",
via_device=(DOMAIN, entry.unique_id),
via_device=(DOMAIN, config_entry.unique_id),
)
else:
# If the remote is no longer available, then delete the device.
Expand All @@ -110,34 +112,31 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

device_registry = dr.async_get(hass)
devices = device_registry.devices.get_devices_for_config_entry_id(
entry.entry_id
config_entry.entry_id
)
for device in devices:
assert device.model is not None
if device.model == "Beoremote One":
device_registry.async_remove_device(device.id)

await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)

# Start WebSocket connection when all entities have been initialized
entry.async_create_background_task(
config_entry.async_create_background_task(
hass,
_start_websocket_listener(hass.data[DOMAIN][entry.entry_id]),
f"{DOMAIN}-{entry.unique_id}-websocket_starter",
_start_websocket_listener(config_entry.runtime_data),
f"{DOMAIN}-{config_entry.unique_id}-websocket_starter",
)

return True


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(
hass: HomeAssistant, config_entry: BangOlufsenConfigEntry
) -> bool:
"""Unload a config entry."""
# Close the API client and WebSocket notification listener
hass.data[DOMAIN][entry.entry_id].client.disconnect_notifications()
await hass.data[DOMAIN][entry.entry_id].client.close_api_client()

unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
config_entry.runtime_data.client.disconnect_notifications()
await config_entry.runtime_data.client.close_api_client()

return unload_ok
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
25 changes: 10 additions & 15 deletions custom_components/bang_olufsen/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,45 @@
from __future__ import annotations

from mozart_api.models import BatteryState
from mozart_api.mozart_client import MozartClient

from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import CONNECTION_STATUS, DOMAIN, WebsocketNotification
from .const import CONNECTION_STATUS, WebsocketNotification
from .entity import BangOlufsenEntity
from .util import BangOlufsenData, set_platform_initialized
from .util import BangOlufsenConfigEntry, set_platform_initialized


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: BangOlufsenConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Binary Sensor entities from config entry."""
data: BangOlufsenData = hass.data[DOMAIN][config_entry.entry_id]
entities: list[BangOlufsenEntity] = []

# Check if device has a battery
battery_state = await data.client.get_battery_state()
battery_state = await config_entry.runtime_data.client.get_battery_state()

if battery_state.battery_level and battery_state.battery_level > 0:
entities.append(
BangOlufsenBinarySensorBatteryCharging(config_entry, data.client)
)
entities.append(BangOlufsenBinarySensorBatteryCharging(config_entry))

async_add_entities(new_entities=entities)

set_platform_initialized(data)
set_platform_initialized(config_entry.runtime_data)


class BangOlufsenBinarySensor(BangOlufsenEntity, BinarySensorEntity):
"""Base Binary Sensor class."""

def __init__(self, entry: ConfigEntry, client: MozartClient) -> None:
def __init__(self, config_entry: BangOlufsenConfigEntry) -> None:
"""Init the Binary Sensor."""
super().__init__(entry, client)
super().__init__(config_entry)

self._attr_is_on = False

Expand All @@ -57,9 +52,9 @@ class BangOlufsenBinarySensorBatteryCharging(BangOlufsenBinarySensor):
_attr_icon = "mdi:battery-charging"
_attr_translation_key = "battery_charging"

def __init__(self, entry: ConfigEntry, client: MozartClient) -> None:
def __init__(self, config_entry: BangOlufsenConfigEntry) -> None:
"""Init the battery charging Binary Sensor."""
super().__init__(entry, client)
super().__init__(config_entry)

self._attr_device_class = BinarySensorDeviceClass.BATTERY_CHARGING
self._attr_unique_id = f"{self._unique_id}-battery-charging"
Expand Down
21 changes: 9 additions & 12 deletions custom_components/bang_olufsen/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,37 @@
from mozart_api.models import Preset

from homeassistant.components.button import ButtonEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import CONNECTION_STATUS, DOMAIN, BangOlufsenSource
from .const import CONNECTION_STATUS, BangOlufsenSource
from .entity import BangOlufsenEntity
from .util import BangOlufsenData, set_platform_initialized
from .util import BangOlufsenConfigEntry, set_platform_initialized


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
config_entry: BangOlufsenConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Button entities from config entry."""
data: BangOlufsenData = hass.data[DOMAIN][config_entry.entry_id]
entities: list[BangOlufsenEntity] = []

# Get available favourites from coordinator.
favourites = data.coordinator.data
favourites = config_entry.runtime_data.coordinator.data

entities.extend(
[
BangOlufsenButtonFavourite(config_entry, data, favourites[favourite])
BangOlufsenButtonFavourite(config_entry, favourites[favourite])
for favourite in favourites
]
)

async_add_entities(new_entities=entities)

set_platform_initialized(data)
set_platform_initialized(config_entry.runtime_data)


class BangOlufsenButton(ButtonEntity, BangOlufsenEntity):
Expand All @@ -53,13 +51,12 @@ class BangOlufsenButtonFavourite(CoordinatorEntity, BangOlufsenButton):

def __init__(
self,
entry: ConfigEntry,
data: BangOlufsenData,
config_entry: BangOlufsenConfigEntry,
favourite: Preset,
) -> None:
"""Init a favourite Button."""
CoordinatorEntity.__init__(self, data.coordinator)
BangOlufsenButton.__init__(self, entry, data.client)
CoordinatorEntity.__init__(self, config_entry.runtime_data.coordinator)
BangOlufsenButton.__init__(self, config_entry)

self._favourite_id: int = int(cast(str, favourite.name)[6:])
self._favourite: Preset = favourite
Expand Down
Loading

0 comments on commit ae1b8f3

Please sign in to comment.