Skip to content

Commit

Permalink
Add Beosound A9 5th manual configuration
Browse files Browse the repository at this point in the history
Add gen Beosound 2 3rd gen manual configuration and proximity sensor
Disable Home Control URI entity by default
Add current sound settings to config flow options
Tweaks
  • Loading branch information
mj23000 committed Apr 26, 2023
1 parent 53a2667 commit 74b3061
Show file tree
Hide file tree
Showing 13 changed files with 44 additions and 48 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ This integration uses the [Mozart open API](https://bang-olufsen.github.io/mozar
Devices that have been tested and _should_ work without any trouble are:

- [Beolab 28](https://www.bang-olufsen.com/en/dk/speakers/beolab-28)
- [Beosound 2 3rd gen](https://www.bang-olufsen.com/en/dk/speakers/beosound-2)
- [Beosound A9 5th gen](https://www.bang-olufsen.com/en/dk/speakers/beosound-a9)
- [Beosound Balance](https://www.bang-olufsen.com/en/dk/speakers/beosound-balance)
- [Beosound Emerge](https://www.bang-olufsen.com/en/dk/speakers/beosound-emerge)
- [Beosound Level](https://www.bang-olufsen.com/en/dk/speakers/beosound-level)
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bangolufsen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ async def init_entities(hass: HomeAssistant, entry: ConfigEntry) -> bool:

# Create the sound mode select entity if supported
# Currently the Balance does not expose any useful Sound Modes and should be excluded
if model != ModelEnum.balance:
if model != ModelEnum.beosound_balance:
listening_modes = client.get_listening_mode_set(async_req=True).get()
if len(listening_modes) > 0:
selects.append(BangOlufsenSelectSoundMode(entry))
Expand Down
12 changes: 7 additions & 5 deletions custom_components/bangolufsen/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ async def async_step_user(
step_id="user",
data_schema=vol.Schema(data_schema),
)

async def async_step_zeroconf(
self, discovery_info: ZeroconfServiceInfo
) -> FlowResult:
Expand All @@ -189,7 +189,6 @@ async def async_step_confirm(
) -> FlowResult:
"""Confirm the configuration of the device."""
if user_input is not None:

# Make sure that all information is included
data = user_input
data[CONF_HOST] = self._host
Expand Down Expand Up @@ -236,6 +235,7 @@ class BangOlufsenOptionsFlowHandler(OptionsFlow):

def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize the options flow handler."""
self._client: MozartClient = MozartClient(host=config_entry.data[CONF_HOST])
self._config_entry: ConfigEntry = config_entry

async def async_step_init(self, user_input: UserInput | None = None) -> FlowResult:
Expand All @@ -250,8 +250,10 @@ async def async_step_init(self, user_input: UserInput | None = None) -> FlowResu
# Check connection
return self.async_create_entry(title=data[CONF_NAME], data=data)

# Create data schema with the last configuration as default values.
# Create data schema with the current volume options, not necessarily the ones set in Home Assistant.
# Also add the ability to change the friendly name in Home Assistant
volume_settings = self._client.get_volume_settings(async_req=True).get()

data_schema = {
vol.Optional(
CONF_NAME, default=self._config_entry.data[CONF_NAME]
Expand All @@ -260,8 +262,8 @@ async def async_step_init(self, user_input: UserInput | None = None) -> FlowResu
data_schema.update(
_config_schema(
volume_step=self._config_entry.data[CONF_VOLUME_STEP],
default_volume=self._config_entry.data[CONF_DEFAULT_VOLUME],
max_volume=self._config_entry.data[CONF_MAX_VOLUME],
default_volume=volume_settings.default.level,
max_volume=volume_settings.maximum.level,
)
)

Expand Down
31 changes: 15 additions & 16 deletions custom_components/bangolufsen/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo, Entity
Expand Down Expand Up @@ -119,10 +119,12 @@ class ModelEnum(StrEnum):
"""Enum for compatible model names."""

beolab_28 = "BeoLab 28"
balance = "Beosound Balance"
emerge = "Beosound Emerge"
level = "Beosound Level"
theatre = "Beosound Theatre"
beosound_2 = "Beosound 2 3rd Gen"
beosound_a9 = "Beosound A9 5th Gen"
beosound_balance = "Beosound Balance"
beosound_emerge = "Beosound Emerge"
beosound_level = "Beosound Level"
beosound_theatre = "Beosound Theatre"


class EntityEnum(StrEnum):
Expand Down Expand Up @@ -181,12 +183,13 @@ class SupportEnum(Enum):

PROXIMITY_SENSOR = (
ModelEnum.beolab_28,
ModelEnum.balance,
ModelEnum.level,
ModelEnum.theatre,
ModelEnum.beosound_2,
ModelEnum.beosound_balance,
ModelEnum.beosound_level,
ModelEnum.beosound_theatre,
)

HOME_CONTROL = (ModelEnum.theatre,)
HOME_CONTROL = (ModelEnum.beosound_theatre,)


DOMAIN: Final[str] = "bangolufsen"
Expand All @@ -196,7 +199,7 @@ class SupportEnum(Enum):
DEFAULT_DEFAULT_VOLUME: Final[int] = 40
DEFAULT_MAX_VOLUME: Final[int] = 100
DEFAULT_VOLUME_STEP: Final[int] = 5
DEFAULT_MODEL: Final[str] = ModelEnum.balance
DEFAULT_MODEL: Final[str] = ModelEnum.beosound_balance

# Acceptable ranges for configuration.
DEFAULT_VOLUME_RANGE: Final[range] = range(1, (70 + 1), 1)
Expand All @@ -217,11 +220,9 @@ class SupportEnum(Enum):
CONF_SERIAL_NUMBER: Final = "serial_number"
CONF_BEOLINK_JID: Final = "jid"


# Models to choose from in manual configuration.
COMPATIBLE_MODELS: list[str] = [x.value for x in ModelEnum]


# Attribute names for zeroconf discovery.
ATTR_TYPE_NUMBER: Final[str] = "tn"
ATTR_SERIAL_NUMBER: Final[str] = "sn"
Expand Down Expand Up @@ -377,8 +378,8 @@ def get_device(hass: HomeAssistant | None, unique_id: str) -> DeviceEntry | None
if not isinstance(hass, HomeAssistant):
return None

registry = device_registry.async_get(hass)
device = cast(DeviceEntry, registry.async_get_device({(DOMAIN, unique_id)}))
device_registry = dr.async_get(hass)
device = cast(DeviceEntry, device_registry.async_get_device({(DOMAIN, unique_id)}))
return device


Expand All @@ -390,7 +391,6 @@ def generate_favourite_attributes(

# Ensure that favourites with volume are properly shown.
for action in favourite.action_list:

if action.type == "volume":
favourite_attribute["volume"] = action.volume_level

Expand Down Expand Up @@ -434,7 +434,6 @@ def generate_favourite_attributes(

# Add queue settings for Deezer queues.
if action.queue_settings:

favourite_attribute["queue_settings"] = {
"repeat": action.queue_settings.repeat,
"shuffle": action.queue_settings.shuffle,
Expand Down
1 change: 0 additions & 1 deletion custom_components/bangolufsen/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ def on_notification_notification(
"""Send notification dispatch."""

if WebSocketNotification.PROXIMITY in notification.value:

async_dispatcher_send(
self.hass,
f"{self._unique_id}_{WebSocketNotification.PROXIMITY}",
Expand Down
7 changes: 3 additions & 4 deletions custom_components/bangolufsen/device_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
CONF_TYPE,
)
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.helpers import device_registry
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.typing import ConfigType

from .const import BANGOLUFSEN_EVENT, DOMAIN, EntityEnum
Expand Down Expand Up @@ -149,8 +149,8 @@ async def async_get_triggers(
triggers = []

# Get the host IP address
registry = device_registry.async_get(hass)
serial_number = list(registry.devices[device_id].identifiers)[0][1]
device_registry = dr.async_get(hass)
serial_number = list(device_registry.devices[device_id].identifiers)[0][1]
media_player: BangOlufsenMediaPlayer = hass.data[DOMAIN][serial_number][
EntityEnum.MEDIA_PLAYER
]
Expand All @@ -167,7 +167,6 @@ async def async_get_triggers(
trigger_types.extend(REMOTE_TRIGGERS)

for trigger_type in trigger_types:

triggers.append(
{
CONF_PLATFORM: "device",
Expand Down
20 changes: 10 additions & 10 deletions custom_components/bangolufsen/manifest.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"domain": "bangolufsen",
"name": "Bang & Olufsen",
"codeowners": ["@mj23000"],
"config_flow": true,
"documentation": "https://github.com/bang-olufsen/bangolufsen-hacs",
"iot_class": "local_push",
"issue_tracker": "https://github.com/bang-olufsen/bangolufsen-hacs/issues",
"requirements": ["mozart-api==2.5.3.123.0"],
"version": "1.0.0",
"zeroconf": ["_bangolufsen._tcp.local."]
"domain": "bangolufsen",
"name": "Bang & Olufsen",
"codeowners": ["@mj23000"],
"config_flow": true,
"documentation": "https://github.com/bang-olufsen/bangolufsen-hacs",
"iot_class": "local_push",
"issue_tracker": "https://github.com/bang-olufsen/bangolufsen-hacs/issues",
"requirements": ["mozart-api==2.5.3.123.0"],
"version": "1.0.1",
"zeroconf": ["_bangolufsen._tcp.local."]
}
7 changes: 1 addition & 6 deletions custom_components/bangolufsen/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,6 @@ def source(self) -> str | None:

# Try to fix some of the source_change chromecast weirdness.
if hasattr(self._playback_metadata, "title"):

# source_change is chromecast but line in is selected.
if self._playback_metadata.title == SourceEnum.lineIn:
return SourceEnum.lineIn
Expand Down Expand Up @@ -964,7 +963,6 @@ async def async_join_players(self, group_members: list[str]) -> None:
jids = []
# Get JID for each group member
for group_member in group_members:

jid = self._get_beolink_jid(group_member)

# Invalid entity
Expand Down Expand Up @@ -1029,7 +1027,6 @@ async def async_play_media(
elif media_type == BangOlufsenMediaType.DEEZER:
try:
if media_id == "flow":

deezer_id = None

if "id" in kwargs[ATTR_MEDIA_EXTRA]:
Expand All @@ -1043,7 +1040,6 @@ async def async_play_media(
else:
# Play a Deezer playlist or album.
if any(match in media_id for match in ("playlist", "album")):

start_from = 0
if "start_from" in kwargs[ATTR_MEDIA_EXTRA]:
start_from = kwargs[ATTR_MEDIA_EXTRA]["start_from"]
Expand Down Expand Up @@ -1075,7 +1071,7 @@ async def async_play_media(

async def async_browse_media(
self,
media_content_type: str | None = None,
media_content_type: MediaType | str | None = None,
media_content_id: str | None = None,
) -> BrowseMedia:
"""Implement the WebSocket media browsing helper."""
Expand Down Expand Up @@ -1157,7 +1153,6 @@ async def async_beolink_leader_command(
"""Send a command to the Beolink leader."""
for command_list in ACCEPTED_COMMANDS_LISTS:
if command in command_list:

# Get the parameter type.
parameter_type = command_list[-1]

Expand Down
2 changes: 1 addition & 1 deletion custom_components/bangolufsen/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

from homeassistant.components.number import NumberEntity, NumberMode
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN, BangOlufsenEntity, EntityEnum, WebSocketNotification
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bangolufsen/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

from homeassistant.components.select import SelectEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN, BangOlufsenEntity, EntityEnum, WebSocketNotification
Expand Down
1 change: 0 additions & 1 deletion custom_components/bangolufsen/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ async def async_added_to_hass(self) -> None:
async def _update_playback_metadata(self, data: PlaybackContentMetadata) -> None:
"""Update Sensor value."""
if data.encoding:

# Ensure that abbreviated formats are capitialized and non-abbreviated formats are made "human readable"
encoding = titleize(underscore(data.encoding))
if data.encoding.capitalize() == encoding:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bangolufsen/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

from homeassistant.components.switch import SwitchDeviceClass, SwitchEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN, BangOlufsenEntity, EntityEnum, WebSocketNotification
Expand Down
3 changes: 2 additions & 1 deletion custom_components/bangolufsen/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

from homeassistant.components.text import TextEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN, BangOlufsenEntity, EntityEnum, WebSocketNotification
Expand Down Expand Up @@ -95,6 +95,7 @@ def __init__(self, entry: ConfigEntry, home_control_uri: str) -> None:
self._attr_unique_id = f"{self._unique_id}-home-control-uri"
self._attr_icon = "mdi:link-variant"
self._attr_native_value = home_control_uri
self._attr_entity_registry_enabled_default = False

async def async_set_value(self, value: str) -> None:
"""Set the Home Control URI name."""
Expand Down

0 comments on commit 74b3061

Please sign in to comment.