From bfaa310b4d6d02631417feadb230159ce9cb1840 Mon Sep 17 00:00:00 2001 From: mj23000 Date: Wed, 6 Nov 2024 14:46:41 +0100 Subject: [PATCH] Add Christian Klits video and blueprints to README Remove old automation examples and Blueprints Add better source checking, ensuring that all sources that are shown are available Add sections to custom services that use JIDs --- README.md | 111 +++--------------- custom_components/bang_olufsen/const.py | 42 ++++--- custom_components/bang_olufsen/coordinator.py | 11 ++ custom_components/bang_olufsen/manifest.json | 2 +- .../bang_olufsen/media_player.py | 9 +- custom_components/bang_olufsen/services.yaml | 55 +++++---- custom_components/bang_olufsen/strings.json | 32 ++++- .../bang_olufsen/translations/en.json | 28 ++++- 8 files changed, 138 insertions(+), 152 deletions(-) diff --git a/README.md b/README.md index c5e6c79..bd3ea98 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,24 @@ This integration can be added to a Home Assistant installation using [HACS](http Afterwards, devices can be added to your Home Assistant installation manually by using the UI or by auto-discovery. +## Example setup and blueprints + +Christian Klit has made a setup video for Home Assistant which includes this integration. +[![Setup video](https://img.youtube.com/vi/ju_gQDIme0w/0.jpg)](https://www.youtube.com/watch?v=ju_gQDIme0w) + +As can be seen in the video, Christian also provides several [automation blueprints](https://cklit.dk/index.php/category/blueprints/mozart/) for this integration on his website. + +These include: + +- Control lights using Beoremote One +- Control shades using Beoremote One +- Announce where music has been joined from (Beolink) +- Control scenes using Light and Control menu items on BR1 BT +- Trigger a scene, when alarm starts +- Show active Listening position and Sound mode on LG screen + +Go to his website [cklit.dk](https://cklit.dk/) to see more information on the useful blueprints for the Mozart and ASE based Bang & Olufsen products. + ## Entities This integration adds an array of different useful entities that are generated and added automatically upon setup, customized for the supported features of the device. Some of these features, such as `proximity sensor` and `home-control` are manually defined based on model name in the code, as they currently can't be determined in any other way. @@ -182,43 +200,6 @@ Event entities are available for each of the compatible keys on the [Beoremote O The favourite buttons correspond to the physical favourite buttons on the device. -### Automation examples - -#### Using the Beoremote One to control lights (OUTDATED) - -```yaml -description: Use the Beoremote One to control living room lights. -mode: single -trigger: - - platform: device - device_id: 234567890abcdef1234567890abcdef1 - domain: bang_olufsen - type: Light/Digit1_KeyPress -condition: [] -action: - - service: light.toggle - target: - entity_id: light.living_room -``` - -#### Setting all devices to standby when leaving home (OUTDATED) - -```yaml -description: Set all Bang & Olufsen devices to standby when leaving home. -mode: single -trigger: - - platform: zone - entity_id: person.example - zone: zone.home - event: leave -condition: [] -action: - - service: bang_olufsen.beolink_allstandby - data: {} - target: - entity_id: media_player.beosound_balance_32836899 -``` - ## Services ### play_media services @@ -461,59 +442,3 @@ Send a media_player command to Beolink leader. ### Service `bang_olufsen.reboot` Reboot the device. - -## Blueprints - -### Announce where music has been joined from (Beolink) - -A Blueprint for this scenario: https://youtu.be/AiZbrYQ6u48 - -Select the speaker you want to announce which other product it joins, when performing a long-press on the Play/Pause button. - -Default phrase is “Joined {friendly name}”, but this can easily be modified to a different language, e.g: “Lytter med fra {friendly name}”. - -Additionally it is possible to enable announcements when using a remote or the Bang & Olufsen app to expand. - -Create an automation for each Mozart product that should announce when joining a different room. - -[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgist.github.com%2Fcklit%2Fe7126c9fda1195bd88bcaefb45fe493e) - -### Control lights using Beoremote One BT (OUTDATED) - -A Blueprint to easily set up light control with Beoremote One BT and a Mozart-based product. - -Select your Mozart product and the light-bulbs or groups you want to control. - -This Blueprint allows for 3 light "zones". See the setup for detailed information on how to control the 3 zones. - -To use Light-commands, press “List” on your Beoremote One, navigate down to “Light” and press the center-button. From here, use the described buttons on the remote below to modify brightness. - -[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgist.github.com%2Fcklit%2F816e6fd144ff91559548e1bf0eb3bf84) - -### Control shades using Beoremote One BT (OUTDATED) - -A Blueprint to easily set up shade control with Beoremote One BT and a Mozart-based product. - -Select your Mozart product and the shades you want to control. - -This Blueprint allows for 3 "zones". See the setup for detailed information on how to control the 3 zones. - -To use Control-commands, press “List” on your Beoremote One, navigate down to “Control” and press the center-button. From here, use the described buttons on the remote below to modify the position. - -[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgist.github.com%2Fcklit%2Fd81d36c525936ab8f9309a226287ff91) - -### Control scenes using Light and Control menu items on Beoremote One BT (OUTDATED) - -A Blueprint to set up scene control with Beoremote One BT and a Mozart-based product. - -Select your Mozart product in the dropdown menu. - -Enter the function (e.g. Light/Func1) you want to trigger the scene with. All functions are described in the Blueprint. - -Select the action that should be triggered by the defined function. This can be any service call, e.g. scene.turn_on. - -In case you have renamed the scenes on your remote, they will not match the documentation. To find the function name of a renamed button, enable the debug mode toggle. Every time a Light or Control item is activated, a notification with the name of the selected function will show up in the Home Assistant dashboard notification panel. We recommend to disable debug mode as soon as the automation is working as expected. - -To activate a scene, press “List” on your Beoremote One BT, navigate down to “Light” or "Control" and press the right-arrow key. Navigate to the function you want to activate and confirm with the center-button. - -[![Open your Home Assistant instance and show the blueprint import dialog with a specific blueprint pre-filled.](https://my.home-assistant.io/badges/blueprint_import.svg)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgist.github.com%2Fcklit%2Fd3ee25fa0576da38ca8dede75cf49c04) diff --git a/custom_components/bang_olufsen/const.py b/custom_components/bang_olufsen/const.py index c7c9233..a5ee405 100644 --- a/custom_components/bang_olufsen/const.py +++ b/custom_components/bang_olufsen/const.py @@ -21,51 +21,71 @@ class BangOlufsenSource: name="Audio Streamer", id="uriStreamer", is_seekable=False, + is_enabled=True, + is_playable=True, ) BLUETOOTH: Final[Source] = Source( name="Bluetooth", id="bluetooth", is_seekable=False, + is_enabled=True, + is_playable=True, ) CHROMECAST: Final[Source] = Source( name="Chromecast built-in", id="chromeCast", is_seekable=False, + is_enabled=True, + is_playable=True, ) LINE_IN: Final[Source] = Source( name="Line-In", id="lineIn", is_seekable=False, + is_enabled=True, + is_playable=True, ) SPDIF: Final[Source] = Source( name="Optical", id="spdif", is_seekable=False, + is_enabled=True, + is_playable=True, ) NET_RADIO: Final[Source] = Source( name="B&O Radio", id="netRadio", is_seekable=False, + is_enabled=True, + is_playable=True, ) DEEZER: Final[Source] = Source( name="Deezer", id="deezer", is_seekable=True, + is_enabled=True, + is_playable=True, ) TIDAL: Final[Source] = Source( name="Tidal", id="tidal", is_seekable=True, + is_enabled=True, + is_playable=True, ) USB_IN: Final[Source] = Source( name="USB", id="usbIn", is_seekable=False, + is_enabled=True, + is_playable=True, ) UNKNOWN: Final[Source] = Source( name="Unknown Source", id="unknown", is_seekable=False, + is_enabled=False, + is_playable=False, ) @@ -203,29 +223,13 @@ class WebsocketNotification(StrEnum): PLAYING: Final[tuple] = ("started", "buffering", BANG_OLUFSEN_ON) NOT_PLAYING: Final[tuple] = ("idle", "paused", "stopped", "ended", "unknown", "error") -# Sources on the device that should not be selectable by the user -HIDDEN_SOURCE_IDS: Final[tuple] = ( - "airPlay", - "bluetooth", - "chromeCast", - "generator", - "local", - "dlna", - "qplay", - "wpl", - "pl", - "beolink", - "classicsAdapter", - "usbIn", -) - # Fallback sources to use in case of API failure. FALLBACK_SOURCES: Final[SourceArray] = SourceArray( items=[ Source( id="uriStreamer", is_enabled=True, - is_playable=False, + is_playable=True, name="Audio Streamer", type=SourceTypeEnum(value="uriStreamer"), is_seekable=False, @@ -233,7 +237,7 @@ class WebsocketNotification(StrEnum): Source( id="bluetooth", is_enabled=True, - is_playable=False, + is_playable=True, name="Bluetooth", type=SourceTypeEnum(value="bluetooth"), is_seekable=False, @@ -241,7 +245,7 @@ class WebsocketNotification(StrEnum): Source( id="spotify", is_enabled=True, - is_playable=False, + is_playable=True, name="Spotify Connect", type=SourceTypeEnum(value="spotify"), is_seekable=True, diff --git a/custom_components/bang_olufsen/coordinator.py b/custom_components/bang_olufsen/coordinator.py index 0705790..d24e215 100644 --- a/custom_components/bang_olufsen/coordinator.py +++ b/custom_components/bang_olufsen/coordinator.py @@ -91,6 +91,9 @@ def __init__( self._client.get_playback_progress_notifications( self.on_playback_progress_notification ) + self._client.get_playback_source_notifications( + self.on_playback_source_notification + ) self._client.get_playback_state_notifications( self.on_playback_state_notification ) @@ -262,6 +265,14 @@ def on_playback_progress_notification(self, notification: PlaybackProgress) -> N notification, ) + def on_playback_source_notification(self, notification: Source) -> None: + """Send playback_source dispatch.""" + async_dispatcher_send( + self.hass, + f"{self._unique_id}_{WebsocketNotification.PLAYBACK_SOURCE}", + notification, + ) + def on_playback_state_notification(self, notification: RenderingState) -> None: """Send playback_state dispatch.""" async_dispatcher_send( diff --git a/custom_components/bang_olufsen/manifest.json b/custom_components/bang_olufsen/manifest.json index 3fa1ab2..4ceec8c 100644 --- a/custom_components/bang_olufsen/manifest.json +++ b/custom_components/bang_olufsen/manifest.json @@ -9,6 +9,6 @@ "iot_class": "local_push", "issue_tracker": "https://github.com/bang-olufsen/bang_olufsen-hacs/issues", "requirements": ["mozart-api==4.1.1.116.0"], - "version": "2.5.5", + "version": "2.5.6", "zeroconf": ["_bangolufsen._tcp.local."] } diff --git a/custom_components/bang_olufsen/media_player.py b/custom_components/bang_olufsen/media_player.py index 0f69262..45767bb 100644 --- a/custom_components/bang_olufsen/media_player.py +++ b/custom_components/bang_olufsen/media_player.py @@ -91,7 +91,6 @@ CONNECTION_STATUS, DOMAIN, FALLBACK_SOURCES, - HIDDEN_SOURCE_IDS, VALID_MEDIA_TYPES, BangOlufsenMediaType, BangOlufsenSource, @@ -288,6 +287,7 @@ async def async_added_to_hass(self) -> None: WebsocketNotification.PLAYBACK_ERROR: self._async_update_playback_error, WebsocketNotification.PLAYBACK_METADATA: self._async_update_playback_metadata_and_beolink, WebsocketNotification.PLAYBACK_PROGRESS: self._async_update_playback_progress, + WebsocketNotification.PLAYBACK_SOURCE: self._async_update_sources, WebsocketNotification.PLAYBACK_STATE: self._async_update_playback_state, WebsocketNotification.REMOTE_MENU_CHANGED: self._async_update_sources, WebsocketNotification.SOURCE_CHANGE: self._async_update_source_change, @@ -381,7 +381,7 @@ async def _async_update_name_and_beolink(self) -> None: self.async_write_ha_state() - async def _async_update_sources(self) -> None: + async def _async_update_sources(self, _: Source | None = None) -> None: """Get sources for the specific product.""" # Audio sources @@ -408,10 +408,7 @@ async def _async_update_sources(self) -> None: self._audio_sources = { source.id: source.name for source in cast(list[Source], sources.items) - if source.is_enabled - and source.id - and source.name - and source.id not in HIDDEN_SOURCE_IDS + if source.is_enabled and source.id and source.name and source.is_playable } # Some sources are not Beolink expandable, meaning that they can't be joined by or expand to other Bang & Olufsen devices for a multi-room experience. diff --git a/custom_components/bang_olufsen/services.yaml b/custom_components/bang_olufsen/services.yaml index eb3e7b4..ce37940 100644 --- a/custom_components/bang_olufsen/services.yaml +++ b/custom_components/bang_olufsen/services.yaml @@ -6,11 +6,14 @@ beolink_join: device: integration: bang_olufsen fields: - beolink_jid: - required: false - example: 1111.2222222.33333333@products.bang-olufsen.com - selector: - text: + jid_options: + collapsed: false + fields: + beolink_jid: + required: false + example: 1111.2222222.33333333@products.bang-olufsen.com + selector: + text: beolink_expand: target: @@ -25,15 +28,18 @@ beolink_expand: example: false selector: boolean: - beolink_jids: - required: false - example: >- - [ - 1111.2222222.33333333@products.bang-olufsen.com, - 4444.5555555.66666666@products.bang-olufsen.com - ] - selector: - object: + jid_options: + collapsed: false + fields: + beolink_jids: + required: false + example: >- + [ + 1111.2222222.33333333@products.bang-olufsen.com, + 4444.5555555.66666666@products.bang-olufsen.com + ] + selector: + object: beolink_unexpand: target: @@ -43,15 +49,18 @@ beolink_unexpand: device: integration: bang_olufsen fields: - beolink_jids: - required: true - example: >- - [ - 1111.2222222.33333333@products.bang-olufsen.com, - 4444.5555555.66666666@products.bang-olufsen.com - ] - selector: - object: + jid_options: + collapsed: false + fields: + beolink_jids: + required: true + example: >- + [ + 1111.2222222.33333333@products.bang-olufsen.com, + 4444.5555555.66666666@products.bang-olufsen.com + ] + selector: + object: beolink_leave: target: diff --git a/custom_components/bang_olufsen/strings.json b/custom_components/bang_olufsen/strings.json index 8bb9483..f7de9b3 100644 --- a/custom_components/bang_olufsen/strings.json +++ b/custom_components/bang_olufsen/strings.json @@ -1,12 +1,14 @@ { "common": { - "short_press_release": "Release of short press", - "long_press_timeout": "Long press", + "jid_options_description": "Advanced grouping options, where devices' unique Beolink IDs (Called JIDs) are used directly. JIDs can be found in the state attributes of the media player entity.", + "jid_options_name": "JID options", + "key_press": "Press", + "key_release": "Release", "long_press_release": "Release of long press", - "very_long_press_timeout": "Very long press", + "long_press_timeout": "Long press", + "short_press_release": "Release of short press", "very_long_press_release": "Release of very long press", - "key_press": "Press", - "key_release": "Release" + "very_long_press_timeout": "Very long press" }, "config": { "error": { @@ -43,6 +45,12 @@ "name": "Beolink JID", "description": "Manually specify Beolink JID to join." } + }, + "sections": { + "jid_options": { + "name": "[%key:component::bang_olufsen::common::jid_options_name%]", + "description": "[%key:component::bang_olufsen::common::jid_options_description%]" + } } }, "beolink_expand": { @@ -57,6 +65,12 @@ "name": "Beolink JIDs", "description": "Specify which Beolink JIDs will join current Beolink experience." } + }, + "sections": { + "jid_options": { + "name": "[%key:component::bang_olufsen::common::jid_options_name%]", + "description": "[%key:component::bang_olufsen::common::jid_options_description%]" + } } }, "beolink_unexpand": { @@ -67,6 +81,12 @@ "name": "Beolink JIDs", "description": "Specify which Beolink JIDs will leave from current Beolink experience." } + }, + "sections": { + "jid_options": { + "name": "[%key:component::bang_olufsen::common::jid_options_name%]", + "description": "[%key:component::bang_olufsen::common::jid_options_description%]" + } } }, "beolink_leave": { @@ -74,7 +94,7 @@ "description": "Leave a Beolink experience." }, "beolink_allstandby": { - "name": "Beolink allstandby", + "name": "Beolink all standby", "description": "Set all Connected Beolink devices to standby." }, "beolink_set_volume": { diff --git a/custom_components/bang_olufsen/translations/en.json b/custom_components/bang_olufsen/translations/en.json index e858f1f..dacd14a 100644 --- a/custom_components/bang_olufsen/translations/en.json +++ b/custom_components/bang_olufsen/translations/en.json @@ -1,5 +1,7 @@ { "common": { + "jid_options_description": "Advanced grouping options, where devices' unique Beolink IDs (Called JIDs) are used directly. JIDs can be found in the state attributes of the media player entity.", + "jid_options_name": "JID options", "key_press": "Press", "key_release": "Release", "long_press_release": "Release of long press", @@ -909,7 +911,7 @@ "services": { "beolink_allstandby": { "description": "Set all Connected Beolink devices to standby.", - "name": "Beolink allstandby" + "name": "Beolink all standby" }, "beolink_expand": { "description": "Expand current Beolink experience.", @@ -923,7 +925,13 @@ "name": "Beolink JIDs" } }, - "name": "Beolink expand" + "name": "Beolink expand", + "sections": { + "jid_options": { + "description": "Advanced grouping options, where devices' unique Beolink IDs (Called JIDs) are used directly. JIDs can be found in the state attributes of the media player entity.", + "name": "JID options" + } + } }, "beolink_join": { "description": "Join a Beolink experience.", @@ -933,7 +941,13 @@ "name": "Beolink JID" } }, - "name": "Beolink join" + "name": "Beolink join", + "sections": { + "jid_options": { + "description": "Advanced grouping options, where devices' unique Beolink IDs (Called JIDs) are used directly. JIDs can be found in the state attributes of the media player entity.", + "name": "JID options" + } + } }, "beolink_leader_command": { "description": "Send a media_player command to Beolink leader.", @@ -981,7 +995,13 @@ "name": "Beolink JIDs" } }, - "name": "Beolink unexpand" + "name": "Beolink unexpand", + "sections": { + "jid_options": { + "description": "Advanced grouping options, where devices' unique Beolink IDs (Called JIDs) are used directly. JIDs can be found in the state attributes of the media player entity.", + "name": "JID options" + } + } }, "reboot": { "description": "Reboot the device.",