Skip to content

Commit

Permalink
Update Beolink actions to HA Core implementations
Browse files Browse the repository at this point in the history
Update icons.json to  2024.9 format
Small tweaks
  • Loading branch information
mj23000 committed Sep 19, 2024
1 parent c796db6 commit c6f3039
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 51 deletions.
3 changes: 2 additions & 1 deletion custom_components/bang_olufsen/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
DEFAULT_MODEL,
DOMAIN,
)
from .util import get_serial_number_from_jid


class EntryData(TypedDict, total=False):
Expand Down Expand Up @@ -107,7 +108,7 @@ async def async_step_user(
)

self._beolink_jid = beolink_self.jid
self._serial_number = beolink_self.jid.split(".")[2].split("@")[0]
self._serial_number = get_serial_number_from_jid(beolink_self.jid)

await self.async_set_unique_id(self._serial_number)
self._abort_if_unique_id_configured()
Expand Down
18 changes: 9 additions & 9 deletions custom_components/bang_olufsen/icons.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"services": {
"beolink_join": "mdi:location-enter",
"beolink_expand": "mdi:location-enter",
"beolink_unexpand": "mdi:location-exit",
"beolink_leave": "mdi:close-circle-outline",
"beolink_allstandby": "mdi:close-circle-multiple-outline",
"beolink_set_volume": "mdi:volume-equal",
"beolink_set_relative_volume": "mdi:volume-plus",
"beolink_leader_command": "mdi:location-enter",
"reboot": "mdi:restart"
"beolink_allstandby": { "service": "mdi:close-circle-multiple-outline" },
"beolink_expand": { "service": "mdi:location-enter" },
"beolink_join": { "service": "mdi:location-enter" },
"beolink_leader_command": { "service": "mdi:location-enter" },
"beolink_leave": { "service": "mdi:close-circle-outline" },
"beolink_set_relative_volume": { "service": "mdi:volume-plus" },
"beolink_set_volume": { "service": "mdi:volume-equal" },
"beolink_unexpand": { "service": "mdi:location-exit" },
"reboot": { "service": "mdi:restart" }
}
}
2 changes: 1 addition & 1 deletion custom_components/bang_olufsen/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"iot_class": "local_push",
"issue_tracker": "https://github.com/bang-olufsen/bang_olufsen-hacs/issues",
"requirements": ["mozart-api==3.4.1.8.7"],
"version": "2.4.0",
"version": "2.4.1",
"zeroconf": ["_bangolufsen._tcp.local."]
}
89 changes: 50 additions & 39 deletions custom_components/bang_olufsen/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
from __future__ import annotations

from collections.abc import Callable
import contextlib
import json
import logging
from typing import Any, cast
from typing import TYPE_CHECKING, Any, cast

from mozart_api import __version__ as MOZART_API_VERSION
from mozart_api.exceptions import ApiException, NotFoundException
Expand Down Expand Up @@ -99,7 +98,7 @@
WebsocketNotification,
)
from .entity import BangOlufsenEntity
from .util import set_platform_initialized
from .util import get_serial_number_from_jid, set_platform_initialized

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -453,30 +452,39 @@ async def _async_update_sources(self) -> None:

self.async_write_ha_state()

def _get_beolink_jid(self, entity_id: str) -> str | None:
def _get_beolink_jid(self, entity_id: str) -> str:
"""Get beolink JID from entity_id."""
jid = None

entity_registry = er.async_get(self.hass)

# Check for valid bang_olufsen media_player entity
entity_entry = entity_registry.async_get(entity_id)
if entity_entry:
config_entry = cast(
ConfigEntry,
self.hass.config_entries.async_get_entry(
cast(str, entity_entry.config_entry_id)
),

if (
entity_entry is None
or entity_entry.domain != Platform.MEDIA_PLAYER
or entity_entry.platform != DOMAIN
or entity_entry.config_entry_id is None
):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_grouping_entity",
translation_placeholders={"entity_id": entity_id},
)

with contextlib.suppress(KeyError):
jid = cast(str, config_entry.data[CONF_BEOLINK_JID])
config_entry = self.hass.config_entries.async_get_entry(
entity_entry.config_entry_id
)
if TYPE_CHECKING:
assert config_entry

return jid
# Return JID
return cast(str, config_entry.data[CONF_BEOLINK_JID])

def _get_entity_id_from_jid(self, jid: str) -> str | None:
"""Get entity_id from Beolink JID (if available)."""

unique_id = jid.split(".")[2].split("@")[0]
unique_id = get_serial_number_from_jid(jid)

entity_registry = er.async_get(self.hass)
return entity_registry.async_get_entity_id(
Expand Down Expand Up @@ -968,23 +976,16 @@ async def async_join_players(self, group_members: list[str]) -> None:
"""Create a Beolink session with defined group members."""

# Use the touch to join if no entities have been defined
# Touch to join will make the device connect to any other currently-playing
# Beolink compatible B&O device.
# Repeated presses / calls will cycle between compatible playing devices.
if len(group_members) == 0:
await self.async_beolink_join()
return

jids = []
# Get JID for each group member
for group_member in group_members:
jid = self._get_beolink_jid(group_member)

# Invalid entity
if jid is None:
_LOGGER.warning("Error adding %s to group", group_member)
continue

jids.append(jid)

await self.async_beolink_expand(beolink_jids=jids)
jids = [self._get_beolink_jid(group_member) for group_member in group_members]
await self.async_beolink_expand(jids)

async def async_unjoin_player(self) -> None:
"""Unjoin Beolink session. End session if leader."""
Expand Down Expand Up @@ -1162,34 +1163,40 @@ async def async_beolink_join(

async def async_beolink_expand(
self, beolink_jids: list[str] | None = None, all_discovered: bool = False
) -> ServiceResponse:
) -> None:
"""Expand a Beolink multi-room experience with a device or devices."""
response: dict[str, Any] = {"not_on_network": []}

# Ensure that the current source is expandable
with contextlib.suppress(KeyError):
if not self._beolink_sources[cast(str, self._source_change.id)]:
return {"invalid_source": self.source}
if not self._beolink_sources[cast(str, self._source_change.id)]:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="invalid_source",
translation_placeholders={
"invalid_source": cast(str, self._source_change.id),
"valid_sources": ", ".join(list(self._beolink_sources.keys())),
},
)

# Expand to all discovered devices
if all_discovered:
peers = await self._client.get_beolink_peers()

for peer in peers:
await self._client.post_beolink_expand(jid=peer.jid)
try:
await self._client.post_beolink_expand(jid=peer.jid)
except NotFoundException:
_LOGGER.warning("Unable to expand to %s", peer.jid)

# Try to expand to all defined devices
elif beolink_jids:
for beolink_jid in beolink_jids:
try:
await self._client.post_beolink_expand(jid=beolink_jid)
except NotFoundException:
response["not_on_network"].append(beolink_jid)

if len(response["not_on_network"]) > 0:
return response

return None
_LOGGER.warning(
"Unable to expand to %s. Is the device available on the network?",
beolink_jid,
)

async def async_beolink_unexpand(self, beolink_jids: list[str]) -> None:
"""Unexpand a Beolink multi-room experience with a device or devices."""
Expand Down Expand Up @@ -1222,6 +1229,8 @@ async def async_beolink_listener_command(
elif parameter_type is None:
await getattr(self, f"async_{command}")()

return

@callback
async def async_beolink_leader_command(
self, command: str, parameter: float | bool | str | None = None
Expand Down Expand Up @@ -1275,6 +1284,8 @@ async def async_beolink_leader_command(
elif parameter_type is None:
await getattr(self, f"async_{command}")()

return

@callback
async def async_beolink_set_volume(self, volume_level: str) -> None:
"""Set volume level for all connected Beolink devices."""
Expand Down
3 changes: 3 additions & 0 deletions custom_components/bang_olufsen/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@
},
"invalid_beolink_parameter": {
"message": "Invalid parameter: {parameter} for {command} which expects a parameter of type: {parameter_type}."
},
"invalid_grouping_entity": {
"message": "Entity with id: {entity_id} can't be added to the Beolink session. Is the entity a Bang & Olufsen media_player?"
}
}
}
3 changes: 3 additions & 0 deletions custom_components/bang_olufsen/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@
"invalid_beolink_parameter": {
"message": "Invalid parameter: {parameter} for {command} which expects a parameter of type: {parameter_type}."
},
"invalid_grouping_entity": {
"message": "Entity with id: {entity_id} can't be added to the Beolink session. Is the entity a Bang & Olufsen media_player?"
},
"invalid_media_type": {
"message": "{invalid_media_type} is an invalid type. Valid values are: {valid_media_types}."
},
Expand Down
5 changes: 5 additions & 0 deletions custom_components/bang_olufsen/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@
def set_platform_initialized(data: BangOlufsenData) -> None:
"""Increment platforms_initialized to indicate that a platform has been initialized."""
data.platforms_initialized += 1


def get_serial_number_from_jid(jid: str) -> str:
"""Get serial number from Beolink JID."""
return jid.split(".")[2].split("@")[0]
2 changes: 1 addition & 1 deletion hacs.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "Bang & Olufsen",
"homeassistant": "2024.4.0",
"homeassistant": "2024.9.0",
"render_readme": true
}

0 comments on commit c6f3039

Please sign in to comment.