Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
|
c113688986 | |
|
8cd2aa3d51 | |
|
bd313ea27a | |
|
0d2424b578 | |
|
9bc8be7861 | |
|
14e30ba970 | |
|
430f6a1840 | |
|
3bcabb8c77 |
|
@ -0,0 +1,32 @@
|
||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
||||||
|
{
|
||||||
|
"name": "Python 3",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
"image": "homeassistant/home-assistant:dev",
|
||||||
|
"postCreateCommand": "scripts/setup",
|
||||||
|
"forwardPorts": [
|
||||||
|
8123
|
||||||
|
],
|
||||||
|
"portsAttributes": {
|
||||||
|
"8123": {
|
||||||
|
"label": "Home Assistant",
|
||||||
|
"onAutoForward": "notify"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Use 'postCreateCommand' to run commands after the container is created.
|
||||||
|
// "postCreateCommand": "pip3 install --user -r requirements.txt",
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
// "customizations": {},
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
|
@ -4,6 +4,11 @@ __pycache__/
|
||||||
*$py.class
|
*$py.class
|
||||||
.DS_store
|
.DS_store
|
||||||
|
|
||||||
|
config
|
||||||
|
.vscode
|
||||||
|
.ruff_cache
|
||||||
|
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
.idea
|
.idea
|
||||||
|
|
55
CHANGELOG.md
55
CHANGELOG.md
|
@ -1,5 +1,60 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.4.1] Media Mystique: The Great Data Disappearing Act!
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Made media data (*track title*, *artist*, *length*) optional to acoomodate soundbars that don't provide this information (🥲)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add translations for the english translation file
|
||||||
|
|
||||||
|
## [0.4.0] Started with an "ick", but is now packed with new features 💪
|
||||||
|
|
||||||
|
> ⚠️ Please read the following carefully:
|
||||||
|
> This release is a bit special. As "something" on Samsung's side changed,
|
||||||
|
> it is currently not possible to retrieve the status of "custom capabilities", eg.
|
||||||
|
> woofer, soundmode, eq, and others. Therefore I decided to give the option to
|
||||||
|
> disable the entities of these features as the value of these entities is not trustworthy.
|
||||||
|
> Instead I implemented all of these and more (thanks to @whitebearded) as service calls.
|
||||||
|
> Have fun using them!
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Configuration flow options for enable / disable
|
||||||
|
- "advanced audio" features (NightMode, Bassmode, VoiceEnhancer)
|
||||||
|
- "woofer" feature
|
||||||
|
- "soundmode" feature
|
||||||
|
- "eq" feature
|
||||||
|
- added `media_player` support for next and previous track
|
||||||
|
- Service calls for:
|
||||||
|
- "advanced audio" features (NightMode, Bassmode, VoiceEnhancer)
|
||||||
|
- "woofer" feature
|
||||||
|
- "soundmode" feature
|
||||||
|
- "speaker_level"
|
||||||
|
- "rear_speaker_mode"
|
||||||
|
- "space_fit_sound"
|
||||||
|
- "active_voice_amplifier"
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Fixed state, also displaying "playing" and "paused" values
|
||||||
|
|
||||||
|
## [0.3.2] Fix division by zero
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- The config flow now also checks whether the `int` provided for `CONF_ENTRY_MAX_VOLUME` is
|
||||||
|
greater than `1` and lower than `100`. This will make sure that a division by zero cannot happen.
|
||||||
|
- Add default value `100` to `CONF_ENTRY_MAX_VOLUME`
|
||||||
|
|
||||||
|
## [0.3.1] Documentation enhancements
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated the `README` as well as the documentation website
|
||||||
|
|
||||||
## [0.3.0] Icons and Chore
|
## [0.3.0] Icons and Chore
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Samuel Spagl
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
5
Pipfile
5
Pipfile
|
@ -9,8 +9,7 @@ rich = "*"
|
||||||
homeassistant = "*"
|
homeassistant = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
black = "*"
|
ruff = "*"
|
||||||
isort = "*"
|
|
||||||
|
|
||||||
[requires]
|
[requires]
|
||||||
python_version = "3.11"
|
python_version = "3.12"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
94
README.md
94
README.md
|
@ -1,6 +1,22 @@
|
||||||
# HomeAssistant: Samsung Soundbar Integration
|
# YASSI: Yet Another Samsung Soundbar Integration (for Home Assistant)
|
||||||
|
|
||||||
> Yet another Samsung Soundbar Integration (YASSI)
|
Welcome to YASSI, the Home Assistant integration designed to bring comprehensive control over your Samsung Soundbar into your smart home ecosystem.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Please use service calls for setting the attribute of a custom capability instead of the entity. (See #43 for more information)
|
||||||
|
|
||||||
|
**Table of Contents:**
|
||||||
|
<!-- TOC -->
|
||||||
|
* [Why YASSI](#why-yassi)
|
||||||
|
* [Features](#features)
|
||||||
|
* [Installation / Setup](#installation--setup)
|
||||||
|
* [Prerequisites](#prerequisites)
|
||||||
|
* [Installation:](#installation)
|
||||||
|
* [Configuration](#configuration)
|
||||||
|
* [Support](#support)
|
||||||
|
* [Contributing](#contributing)
|
||||||
|
* [General Thanks](#general-thanks)
|
||||||
|
<!-- TOC -->
|
||||||
|
|
||||||
## Why YASSI
|
## Why YASSI
|
||||||
|
|
||||||
|
@ -18,48 +34,54 @@ are not documented... ;)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Set-Up through HomeAssistant-UI
|
|
||||||
- Theoretically it should be possible to have multiple Devices (not tested)
|
|
||||||
|
|
||||||
- `media_player` Entity
|
- **UI Setup**: You can easily set up your Soundbar through the UI.
|
||||||
- On / Off
|
- **Media Player Controls**: Power, volume, mute, source selection, and media controls are all at your fingertips.
|
||||||
- Volume
|
- **Selectable Sound Modes**: Choose from various sound modes and inputs for optimal audio.
|
||||||
- Mute
|
- **Subwoofer & Equalizer Adjustment**: Fine-tune your audio experience.
|
||||||
- Input Source
|
- **Switchable Enhancements**: Toggle features like night mode and voice amplification.
|
||||||
- Sound Mode
|
- **Customizable Bass Level**: Set the bass to your preference.
|
||||||
- Media
|
- **Multiple Devices**: should be theoretically possible but **not** tested
|
||||||
- Play / Pause / Stop
|
|
||||||
- Artist
|
|
||||||
- Title
|
|
||||||
- Music Cover Art url (iTunes Api)
|
|
||||||
- `switch` entity
|
|
||||||
- Night mode
|
|
||||||
- Bass mode
|
|
||||||
- Voice amplifier
|
|
||||||
- `number` entity
|
|
||||||
- bass level
|
|
||||||
- *[to come] equalizer bands*
|
|
||||||
- `select` entity
|
|
||||||
- sound mode (additional control in the "Device" tab)
|
|
||||||
- input (additional control in the "Device" tab)
|
|
||||||
- equalizer preset
|
|
||||||
|
|
||||||
## How to install it:
|
For the full feature list per entity type, please take a look at the [documentation](ha-samsung-soundbar.vercel.app) website.
|
||||||
|
|
||||||
### HACS:
|
## Installation / Setup
|
||||||
> ⚠️ not done yet but planned (hopefully)
|
|
||||||
|
|
||||||
### Adding this repository as custom repository
|
### Prerequisites
|
||||||
|
|
||||||
Add this repository as custom repository in HACS and install it ;)
|
Before you begin, ensure you have the following:
|
||||||
|
|
||||||
### Manual
|
- A Samsung Soundbar compatible with SmartThings.
|
||||||
|
- Home Assistant installed and running.
|
||||||
|
- HACS (Home Assistant Community Store) for easy installation.
|
||||||
|
|
||||||
You can also copy the `samsung_soundbar` folder in the `custom_components` folder to
|
### Installation
|
||||||
your `config/custom_components` folder.
|
|
||||||
|
|
||||||
|
1. Add this repository as a custom repository in HACS or manually copy the `samsung_soundbar` folder to the `custom_components` directory in your Home Assistant configuration.
|
||||||
|
|
||||||
|
[](https://my.home-assistant.io/redirect/hacs_repository/?owner=samuelspagl&repository=ha_samsung_soundbar&category=integration)
|
||||||
|
2. Restart Home Assistant.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> It is planned to add it to the default `HACS` repository list, but not done yet.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
To integrate your Samsung Soundbar with Home Assistant using YASSI, you will be asked for the following variables:
|
||||||
|
|
||||||
|
- **SmartThings API Key**: [Retrieve your API key from SmartThings Tokens.](https://account.smartthings.com/tokens)
|
||||||
|
- **Device ID**: [Find your device ID at SmartThings Devices.](https://my.smartthings.com/advanced/devices)
|
||||||
|
- **Device Name**: Choose a name for your soundbar to be recognized in Home Assistant.
|
||||||
|
- **Max Volume**: Define the maximum volume level for the `media_player` slider (between `1` and `100`).
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
For support, feature requests, or bug reporting, please visit the Issues section of this GitHub repository.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
|
||||||
|
|
||||||
## General Thanks
|
## General Thanks
|
||||||
|
|
||||||
Like already mentioned, thanks to @PiotrMachowski / @thierryBourbon for the general
|
- Like already mentioned, thanks to @PiotrMachowski / @thierryBourbon for the general idea on how to do things.
|
||||||
idea on how to do things.
|
|
||||||
|
|
|
@ -6,9 +6,18 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from pysmartthings import SmartThings
|
from pysmartthings import SmartThings
|
||||||
|
|
||||||
from .api_extension.SoundbarDevice import SoundbarDevice
|
from .api_extension.SoundbarDevice import SoundbarDevice
|
||||||
from .const import (CONF_ENTRY_API_KEY, CONF_ENTRY_DEVICE_ID,
|
from .const import (
|
||||||
CONF_ENTRY_DEVICE_NAME, CONF_ENTRY_MAX_VOLUME, DOMAIN,
|
CONF_ENTRY_API_KEY,
|
||||||
SUPPORTED_DOMAINS)
|
CONF_ENTRY_DEVICE_ID,
|
||||||
|
CONF_ENTRY_DEVICE_NAME,
|
||||||
|
CONF_ENTRY_MAX_VOLUME,
|
||||||
|
CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES,
|
||||||
|
CONF_ENTRY_SETTINGS_EQ_SELECTOR,
|
||||||
|
CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR,
|
||||||
|
CONF_ENTRY_SETTINGS_WOOFER_NUMBER,
|
||||||
|
DOMAIN,
|
||||||
|
SUPPORTED_DOMAINS,
|
||||||
|
)
|
||||||
from .models import DeviceConfig, SoundbarConfig
|
from .models import DeviceConfig, SoundbarConfig
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -21,7 +30,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
# store shell object
|
# store shell object
|
||||||
|
|
||||||
_LOGGER.info(f"[{DOMAIN}] Starting to setup a ConfigEntry")
|
_LOGGER.info(f"[{DOMAIN}] Starting to setup a ConfigEntry")
|
||||||
_LOGGER.debug(f"[{DOMAIN}] Setting up ConfigEntry with the following data: {entry.data}")
|
_LOGGER.debug(
|
||||||
|
f"[{DOMAIN}] Setting up ConfigEntry with the following data: {entry.data}"
|
||||||
|
)
|
||||||
if not DOMAIN in hass.data:
|
if not DOMAIN in hass.data:
|
||||||
_LOGGER.debug(f"[{DOMAIN}] Domain not found in hass.data setting default")
|
_LOGGER.debug(f"[{DOMAIN}] Domain not found in hass.data setting default")
|
||||||
hass.data[DOMAIN] = SoundbarConfig(
|
hass.data[DOMAIN] = SoundbarConfig(
|
||||||
|
@ -48,6 +59,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
session,
|
session,
|
||||||
entry.data.get(CONF_ENTRY_MAX_VOLUME),
|
entry.data.get(CONF_ENTRY_MAX_VOLUME),
|
||||||
entry.data.get(CONF_ENTRY_DEVICE_NAME),
|
entry.data.get(CONF_ENTRY_DEVICE_NAME),
|
||||||
|
enable_eq=entry.data.get(CONF_ENTRY_SETTINGS_EQ_SELECTOR),
|
||||||
|
enable_advanced_audio=entry.data.get(
|
||||||
|
CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES
|
||||||
|
),
|
||||||
|
enable_soundmode=entry.data.get(CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR),
|
||||||
|
enable_woofer=entry.data.get(CONF_ENTRY_SETTINGS_WOOFER_NUMBER),
|
||||||
)
|
)
|
||||||
await soundbar_device.update()
|
await soundbar_device.update()
|
||||||
domain_config.devices[entry.data.get(CONF_ENTRY_DEVICE_ID)] = DeviceConfig(
|
domain_config.devices[entry.data.get(CONF_ENTRY_DEVICE_ID)] = DeviceConfig(
|
||||||
|
|
|
@ -2,11 +2,11 @@ import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
|
||||||
from pysmartthings import DeviceEntity
|
from pysmartthings import DeviceEntity
|
||||||
|
|
||||||
|
from .const import SpeakerIdentifier, RearSpeakerMode
|
||||||
from ..const import DOMAIN
|
from ..const import DOMAIN
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -14,7 +14,15 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
class SoundbarDevice:
|
class SoundbarDevice:
|
||||||
def __init__(
|
def __init__(
|
||||||
self, device: DeviceEntity, session, max_volume: int, device_name: str
|
self,
|
||||||
|
device: DeviceEntity,
|
||||||
|
session,
|
||||||
|
max_volume: int,
|
||||||
|
device_name: str,
|
||||||
|
enable_eq: bool = False,
|
||||||
|
enable_soundmode: bool = False,
|
||||||
|
enable_advanced_audio: bool = False,
|
||||||
|
enable_woofer: bool = False,
|
||||||
):
|
):
|
||||||
self.device = device
|
self.device = device
|
||||||
self._device_id = self.device.device_id
|
self._device_id = self.device.device_id
|
||||||
|
@ -22,17 +30,21 @@ class SoundbarDevice:
|
||||||
self.__session = session
|
self.__session = session
|
||||||
self.__device_name = device_name
|
self.__device_name = device_name
|
||||||
|
|
||||||
|
self.__enable_soundmode = enable_soundmode
|
||||||
self.__supported_soundmodes = []
|
self.__supported_soundmodes = []
|
||||||
self.__active_soundmode = ""
|
self.__active_soundmode = ""
|
||||||
|
|
||||||
|
self.__enable_woofer = enable_woofer
|
||||||
self.__woofer_level = 0
|
self.__woofer_level = 0
|
||||||
self.__woofer_connection = ""
|
self.__woofer_connection = ""
|
||||||
|
|
||||||
|
self.__enable_eq = enable_eq
|
||||||
self.__active_eq_preset = ""
|
self.__active_eq_preset = ""
|
||||||
self.__supported_eq_presets = []
|
self.__supported_eq_presets = []
|
||||||
self.__eq_action = ""
|
self.__eq_action = ""
|
||||||
self.__eq_bands = []
|
self.__eq_bands = []
|
||||||
|
|
||||||
|
self.__enable_advanced_audio = enable_advanced_audio
|
||||||
self.__voice_amplifier = 0
|
self.__voice_amplifier = 0
|
||||||
self.__night_mode = 0
|
self.__night_mode = 0
|
||||||
self.__bass_mode = 0
|
self.__bass_mode = 0
|
||||||
|
@ -49,12 +61,18 @@ class SoundbarDevice:
|
||||||
await self.device.status.refresh()
|
await self.device.status.refresh()
|
||||||
|
|
||||||
await self._update_media()
|
await self._update_media()
|
||||||
|
|
||||||
|
if self.__enable_soundmode:
|
||||||
await self._update_soundmode()
|
await self._update_soundmode()
|
||||||
|
if self.__enable_advanced_audio:
|
||||||
await self._update_advanced_audio()
|
await self._update_advanced_audio()
|
||||||
|
if self.__enable_soundmode:
|
||||||
await self._update_woofer()
|
await self._update_woofer()
|
||||||
|
if self.__enable_eq:
|
||||||
await self._update_equalizer()
|
await self._update_equalizer()
|
||||||
|
|
||||||
async def _update_media(self):
|
async def _update_media(self):
|
||||||
|
if "audioTrackData" in self.device.status._attributes:
|
||||||
self.__media_artist = self.device.status._attributes["audioTrackData"].value[
|
self.__media_artist = self.device.status._attributes["audioTrackData"].value[
|
||||||
"artist"
|
"artist"
|
||||||
]
|
]
|
||||||
|
@ -70,14 +88,14 @@ class SoundbarDevice:
|
||||||
|
|
||||||
async def _update_soundmode(self):
|
async def _update_soundmode(self):
|
||||||
await self.update_execution_data(["/sec/networkaudio/soundmode"])
|
await self.update_execution_data(["/sec/networkaudio/soundmode"])
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(1)
|
||||||
payload = await self.get_execute_status()
|
payload = await self.get_execute_status()
|
||||||
retry = 0
|
retry = 0
|
||||||
while (
|
while (
|
||||||
"x.com.samsung.networkaudio.supportedSoundmode" not in payload
|
"x.com.samsung.networkaudio.supportedSoundmode" not in payload
|
||||||
and retry < 10
|
and retry < 10
|
||||||
):
|
):
|
||||||
await asyncio.sleep(0.2)
|
await asyncio.sleep(1)
|
||||||
payload = await self.get_execute_status()
|
payload = await self.get_execute_status()
|
||||||
retry += 1
|
retry += 1
|
||||||
if retry == 10:
|
if retry == 10:
|
||||||
|
@ -179,7 +197,15 @@ class SoundbarDevice:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self) -> str:
|
def state(self) -> str:
|
||||||
return "on" if self.device.status.switch else "off"
|
if self.device.status.switch:
|
||||||
|
if self.device.status.playback_status == "playing":
|
||||||
|
return "playing"
|
||||||
|
if self.device.status.playback_status == "paused":
|
||||||
|
return "paused"
|
||||||
|
else:
|
||||||
|
return "on"
|
||||||
|
else:
|
||||||
|
return "off"
|
||||||
|
|
||||||
async def switch_off(self):
|
async def switch_off(self):
|
||||||
await self.device.switch_off(True)
|
await self.device.switch_off(True)
|
||||||
|
@ -347,11 +373,15 @@ class SoundbarDevice:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_duration(self) -> int | None:
|
def media_duration(self) -> int | None:
|
||||||
return self.device.status.attributes.get("totalTime").value
|
attr = self.device.status.attributes.get("totalTime", None)
|
||||||
|
if attr:
|
||||||
|
return attr.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_position(self) -> int | None:
|
def media_position(self) -> int | None:
|
||||||
return self.device.status.attributes.get("elapsedTime").value
|
attr = self.device.status.attributes.get("elapsedTime", None)
|
||||||
|
if attr:
|
||||||
|
return attr.value
|
||||||
|
|
||||||
async def media_play(self):
|
async def media_play(self):
|
||||||
await self.device.play(True)
|
await self.device.play(True)
|
||||||
|
@ -362,6 +392,12 @@ class SoundbarDevice:
|
||||||
async def media_stop(self):
|
async def media_stop(self):
|
||||||
await self.device.stop(True)
|
await self.device.stop(True)
|
||||||
|
|
||||||
|
async def media_next_track(self):
|
||||||
|
await self.device.command("main", "mediaPlayback", "fastForward")
|
||||||
|
|
||||||
|
async def media_previous_track(self):
|
||||||
|
await self.device.command("main", "mediaPlayback", "rewind")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def media_app_name(self):
|
def media_app_name(self):
|
||||||
detail_status = self.device.status.attributes.get("detailName", None)
|
detail_status = self.device.status.attributes.get("detailName", None)
|
||||||
|
@ -373,21 +409,54 @@ class SoundbarDevice:
|
||||||
def media_coverart_updated(self) -> datetime.datetime:
|
def media_coverart_updated(self) -> datetime.datetime:
|
||||||
return self.__media_cover_url_update_time
|
return self.__media_cover_url_update_time
|
||||||
|
|
||||||
|
# ------------ Speaker Level ----------------
|
||||||
|
|
||||||
|
async def set_speaker_level(self, speaker: SpeakerIdentifier, level: int):
|
||||||
|
await self.set_custom_execution_data(
|
||||||
|
href="/sec/networkaudio/channelVolume",
|
||||||
|
property="x.com.samsung.networkaudio.channelVolume",
|
||||||
|
value=[{"name": speaker.value, "value": level}],
|
||||||
|
)
|
||||||
|
|
||||||
|
async def set_rear_speaker_mode(self, mode: RearSpeakerMode):
|
||||||
|
await self.set_custom_execution_data(
|
||||||
|
href="/sec/networkaudio/surroundspeaker",
|
||||||
|
property="x.com.samsung.networkaudio.currentRearPosition",
|
||||||
|
value=mode.value,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------ OTHER FUNCTIONS ------------
|
||||||
|
|
||||||
|
async def set_active_voice_amplifier(self, enabled: bool):
|
||||||
|
await self.set_custom_execution_data(
|
||||||
|
href="/sec/networkaudio/activeVoiceAmplifier",
|
||||||
|
property="x.com.samsung.networkaudio.activeVoiceAmplifier",
|
||||||
|
value=1 if enabled else 0
|
||||||
|
)
|
||||||
|
|
||||||
|
async def set_space_fit_sound(self, enabled: bool):
|
||||||
|
await self.set_custom_execution_data(
|
||||||
|
href="/sec/networkaudio/spacefitSound",
|
||||||
|
property="x.com.samsung.networkaudio.spacefitSound",
|
||||||
|
value=1 if enabled else 0
|
||||||
|
)
|
||||||
|
|
||||||
# ------------ SUPPORT FUNCTIONS ------------
|
# ------------ SUPPORT FUNCTIONS ------------
|
||||||
|
|
||||||
async def update_execution_data(self, argument: str):
|
async def update_execution_data(self, argument: str):
|
||||||
return await self.device.command("main", "execute", "execute", argument)
|
stuff = await self.device.command("main", "execute", "execute", argument)
|
||||||
|
return stuff
|
||||||
|
|
||||||
async def set_custom_execution_data(self, href: str, property: str, value):
|
async def set_custom_execution_data(self, href: str, property: str, value):
|
||||||
argument = [href, {property: value}]
|
argument = [href, {property: value}]
|
||||||
await self.device.command("main", "execute", "execute", argument)
|
assert await self.device.command("main", "execute", "execute", argument)
|
||||||
|
|
||||||
async def get_execute_status(self):
|
async def get_execute_status(self):
|
||||||
url = f"https://api.smartthings.com/v1/devices/{self._device_id}/components/main/capabilities/execute/status"
|
url = f"https://api.smartthings.com/v1/devices/{self._device_id}/components/main/capabilities/execute/status"
|
||||||
request_headers = {"Authorization": "Bearer " + self._api_key}
|
request_headers = {"Authorization": "Bearer " + self._api_key}
|
||||||
resp = await self.__session.get(url, headers=request_headers)
|
resp = await self.__session.get(url, headers=request_headers)
|
||||||
dict = await resp.json()
|
dict_stuff = await resp.json()
|
||||||
return dict["data"]["value"]["payload"]
|
return dict_stuff["data"]["value"]["payload"]
|
||||||
|
|
||||||
async def get_song_title_artwork(self, artist: str, title: str) -> str:
|
async def get_song_title_artwork(self, artist: str, title: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class SpeakerIdentifier(Enum):
|
||||||
|
CENTER = "Spk_Center"
|
||||||
|
SIDE = "Spk_Side"
|
||||||
|
WIDE = "Spk_Wide"
|
||||||
|
FRONT_TOP = "Spk_Front_Top"
|
||||||
|
REAR = "Spk_Rear"
|
||||||
|
REAR_TOP = "Spk_Rear_Top"
|
||||||
|
|
||||||
|
|
||||||
|
class RearSpeakerMode(Enum):
|
||||||
|
FRONT = "Front"
|
||||||
|
REAR = "Rear"
|
|
@ -1,13 +1,24 @@
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import pysmartthings
|
import pysmartthings
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from pysmartthings import APIResponseError
|
from pysmartthings import APIResponseError
|
||||||
|
from voluptuous import All, Range
|
||||||
|
|
||||||
from .const import (CONF_ENTRY_API_KEY, CONF_ENTRY_DEVICE_ID,
|
from .const import (
|
||||||
CONF_ENTRY_DEVICE_NAME, CONF_ENTRY_MAX_VOLUME, DOMAIN)
|
CONF_ENTRY_API_KEY,
|
||||||
|
CONF_ENTRY_DEVICE_ID,
|
||||||
|
CONF_ENTRY_DEVICE_NAME,
|
||||||
|
CONF_ENTRY_MAX_VOLUME,
|
||||||
|
CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES,
|
||||||
|
CONF_ENTRY_SETTINGS_EQ_SELECTOR,
|
||||||
|
CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR,
|
||||||
|
CONF_ENTRY_SETTINGS_WOOFER_NUMBER,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -23,20 +34,8 @@ async def validate_input(api, device_id: str):
|
||||||
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
async def async_step_user(self, user_input=None):
|
async def async_step_user(self, user_input=None):
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
try:
|
self.user_input = user_input
|
||||||
session = async_get_clientsession(self.hass)
|
return await self.async_step_device()
|
||||||
api = pysmartthings.SmartThings(
|
|
||||||
session, user_input.get(CONF_ENTRY_API_KEY)
|
|
||||||
)
|
|
||||||
device = await validate_input(api, user_input.get(CONF_ENTRY_DEVICE_ID))
|
|
||||||
|
|
||||||
_LOGGER.debug(
|
|
||||||
f"Successfully validated Input, Creating entry with title {DOMAIN} and data {user_input}"
|
|
||||||
)
|
|
||||||
return self.async_create_entry(title=DOMAIN, data=user_input)
|
|
||||||
except Exception as excp:
|
|
||||||
_LOGGER.error(f"The ConfigFlow triggered an exception {excp}")
|
|
||||||
return self.async_abort(reason="fetch_failed")
|
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="user",
|
||||||
|
@ -45,7 +44,98 @@ class ExampleConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
vol.Required(CONF_ENTRY_API_KEY): str,
|
vol.Required(CONF_ENTRY_API_KEY): str,
|
||||||
vol.Required(CONF_ENTRY_DEVICE_ID): str,
|
vol.Required(CONF_ENTRY_DEVICE_ID): str,
|
||||||
vol.Required(CONF_ENTRY_DEVICE_NAME): str,
|
vol.Required(CONF_ENTRY_DEVICE_NAME): str,
|
||||||
vol.Required(CONF_ENTRY_MAX_VOLUME): int,
|
vol.Required(CONF_ENTRY_MAX_VOLUME, default=100): All(
|
||||||
|
int, Range(min=1, max=100)
|
||||||
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def async_step_device(self, user_input: dict[str, any] | None = None):
|
||||||
|
if user_input is not None:
|
||||||
|
self.user_input.update(user_input)
|
||||||
|
|
||||||
|
try:
|
||||||
|
session = async_get_clientsession(self.hass)
|
||||||
|
api = pysmartthings.SmartThings(
|
||||||
|
session, self.user_input.get(CONF_ENTRY_API_KEY)
|
||||||
|
)
|
||||||
|
device = await validate_input(
|
||||||
|
api, self.user_input.get(CONF_ENTRY_DEVICE_ID)
|
||||||
|
)
|
||||||
|
_LOGGER.debug(
|
||||||
|
f"Successfully validated Input, Creating entry with title {DOMAIN} and data {user_input}"
|
||||||
|
)
|
||||||
|
except Exception as excp:
|
||||||
|
_LOGGER.error(f"The ConfigFlow triggered an exception {excp}")
|
||||||
|
return self.async_abort(reason="fetch_failed")
|
||||||
|
return self.async_create_entry(title=DOMAIN, data=self.user_input)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="device",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES): bool,
|
||||||
|
vol.Required(CONF_ENTRY_SETTINGS_EQ_SELECTOR): bool,
|
||||||
|
vol.Required(CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR): bool,
|
||||||
|
vol.Required(CONF_ENTRY_SETTINGS_WOOFER_NUMBER): bool,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_reconfigure(self, user_input: dict[str, Any] | None = None):
|
||||||
|
"""Handle a reconfiguration flow initialized by the user."""
|
||||||
|
self.config_entry = self.hass.config_entries.async_get_entry(
|
||||||
|
self.context["entry_id"]
|
||||||
|
)
|
||||||
|
return await self.async_step_reconfigure_confirm()
|
||||||
|
|
||||||
|
async def async_step_reconfigure_confirm(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
):
|
||||||
|
"""Handle a reconfiguration flow initialized by the user."""
|
||||||
|
errors: dict[str, str] = {}
|
||||||
|
assert self.config_entry
|
||||||
|
|
||||||
|
if user_input is not None:
|
||||||
|
return self.async_update_reload_and_abort(
|
||||||
|
self.config_entry,
|
||||||
|
data={**self.config_entry.data, **user_input},
|
||||||
|
reason="reconfigure_successful",
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="reconfigure_confirm",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(
|
||||||
|
CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES,
|
||||||
|
default=self.config_entry.data.get(
|
||||||
|
CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES
|
||||||
|
),
|
||||||
|
): bool,
|
||||||
|
vol.Required(
|
||||||
|
CONF_ENTRY_SETTINGS_EQ_SELECTOR,
|
||||||
|
default=self.config_entry.data.get(
|
||||||
|
CONF_ENTRY_SETTINGS_EQ_SELECTOR
|
||||||
|
),
|
||||||
|
): bool,
|
||||||
|
vol.Required(
|
||||||
|
CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR,
|
||||||
|
default=self.config_entry.data.get(
|
||||||
|
CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR
|
||||||
|
),
|
||||||
|
): bool,
|
||||||
|
vol.Required(
|
||||||
|
CONF_ENTRY_SETTINGS_WOOFER_NUMBER,
|
||||||
|
default=self.config_entry.data.get(
|
||||||
|
CONF_ENTRY_SETTINGS_WOOFER_NUMBER
|
||||||
|
),
|
||||||
|
): bool,
|
||||||
|
vol.Required(CONF_ENTRY_MAX_VOLUME, default=100): All(
|
||||||
|
int, Range(min=1, max=100)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
errors=errors,
|
||||||
|
)
|
||||||
|
|
|
@ -9,6 +9,12 @@ CONF_ENTRY_API_KEY = "api_key"
|
||||||
CONF_ENTRY_DEVICE_ID = "device_id"
|
CONF_ENTRY_DEVICE_ID = "device_id"
|
||||||
CONF_ENTRY_DEVICE_NAME = "device_name"
|
CONF_ENTRY_DEVICE_NAME = "device_name"
|
||||||
CONF_ENTRY_MAX_VOLUME = "device_volume"
|
CONF_ENTRY_MAX_VOLUME = "device_volume"
|
||||||
|
|
||||||
|
CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES = "settings_advanced_audio"
|
||||||
|
CONF_ENTRY_SETTINGS_EQ_SELECTOR = "settings_eq"
|
||||||
|
CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR = "settings_soundmode"
|
||||||
|
CONF_ENTRY_SETTINGS_WOOFER_NUMBER = "settings_woofer"
|
||||||
|
|
||||||
DEFAULT_NAME = DOMAIN
|
DEFAULT_NAME = DOMAIN
|
||||||
|
|
||||||
BUTTON = BUTTON_DOMAIN
|
BUTTON = BUTTON_DOMAIN
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
{
|
{
|
||||||
"domain": "samsung_soundbar",
|
"domain": "samsung_soundbar",
|
||||||
"name": "Samsung Soundbar",
|
"name": "Samsung Soundbar",
|
||||||
"codeowners": ["@samuelspagl"],
|
"codeowners": [
|
||||||
|
"@samuelspagl"
|
||||||
|
],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.example.com",
|
"documentation": "https://www.example.com",
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
"issue_tracker": "https://github.com/samuelspagl/ha_samsung_soundbar/issues",
|
"issue_tracker": "https://github.com/samuelspagl/ha_samsung_soundbar/issues",
|
||||||
"requirements": ["pysmartthings"],
|
"requirements": [
|
||||||
"version": "0.3.0"
|
"pysmartthings==0.7.8"
|
||||||
|
],
|
||||||
|
"version": "0.4.1"
|
||||||
}
|
}
|
|
@ -1,16 +1,25 @@
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Mapping
|
from typing import Any, Mapping
|
||||||
|
|
||||||
from homeassistant.components.media_player import (DEVICE_CLASS_SPEAKER,
|
from homeassistant.components.media_player import (
|
||||||
MediaPlayerEntity)
|
DEVICE_CLASS_SPEAKER,
|
||||||
from homeassistant.components.media_player.const import \
|
MediaPlayerEntity,
|
||||||
MediaPlayerEntityFeature
|
)
|
||||||
|
from homeassistant.components.media_player.const import MediaPlayerEntityFeature
|
||||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||||
from homeassistant.helpers.entity import DeviceInfo, generate_entity_id
|
from homeassistant.helpers.entity import DeviceInfo, generate_entity_id
|
||||||
|
from homeassistant.helpers import config_validation as cv, entity_platform, selector
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
from .api_extension.SoundbarDevice import SoundbarDevice
|
from .api_extension.SoundbarDevice import SoundbarDevice
|
||||||
from .const import (CONF_ENTRY_API_KEY, CONF_ENTRY_DEVICE_ID,
|
from .api_extension.const import SpeakerIdentifier, RearSpeakerMode
|
||||||
CONF_ENTRY_DEVICE_NAME, CONF_ENTRY_MAX_VOLUME, DOMAIN)
|
from .const import (
|
||||||
|
CONF_ENTRY_API_KEY,
|
||||||
|
CONF_ENTRY_DEVICE_ID,
|
||||||
|
CONF_ENTRY_DEVICE_NAME,
|
||||||
|
CONF_ENTRY_MAX_VOLUME,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
from .models import DeviceConfig
|
from .models import DeviceConfig
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -27,14 +36,82 @@ SUPPORT_SMARTTHINGS_SOUNDBAR = (
|
||||||
| MediaPlayerEntityFeature.TURN_OFF
|
| MediaPlayerEntityFeature.TURN_OFF
|
||||||
| MediaPlayerEntityFeature.TURN_ON
|
| MediaPlayerEntityFeature.TURN_ON
|
||||||
| MediaPlayerEntityFeature.PLAY
|
| MediaPlayerEntityFeature.PLAY
|
||||||
|
| MediaPlayerEntityFeature.NEXT_TRACK
|
||||||
|
| MediaPlayerEntityFeature.PREVIOUS_TRACK
|
||||||
| MediaPlayerEntityFeature.STOP
|
| MediaPlayerEntityFeature.STOP
|
||||||
| MediaPlayerEntityFeature.SELECT_SOUND_MODE
|
| MediaPlayerEntityFeature.SELECT_SOUND_MODE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def addServices():
|
||||||
|
platform = entity_platform.async_get_current_platform()
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"select_soundmode",
|
||||||
|
cv.make_entity_service_schema({vol.Required("sound_mode"): str}),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_select_sound_mode.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"set_woofer_level",
|
||||||
|
cv.make_entity_service_schema(
|
||||||
|
{vol.Required("level"): vol.All(int, vol.Range(min=-12, max=6))}
|
||||||
|
),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_set_woofer_level.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"set_night_mode",
|
||||||
|
cv.make_entity_service_schema({vol.Required("enabled"): bool}),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_set_night_mode.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"set_bass_enhancer",
|
||||||
|
cv.make_entity_service_schema({vol.Required("enabled"): bool}),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_set_bass_mode.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"set_voice_enhancer",
|
||||||
|
cv.make_entity_service_schema({vol.Required("enabled"): bool}),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_set_voice_mode.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"set_speaker_level",
|
||||||
|
cv.make_entity_service_schema(
|
||||||
|
{vol.Required("speaker_identifier"): str, vol.Required("level"): int}
|
||||||
|
),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_set_speaker_level.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"set_rear_speaker_mode",
|
||||||
|
cv.make_entity_service_schema({vol.Required("speaker_mode"): str}),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_set_rear_speaker_mode.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"set_active_voice_amplifier",
|
||||||
|
cv.make_entity_service_schema({vol.Required("enabled"): bool}),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_set_active_voice_amplifier.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
platform.async_register_entity_service(
|
||||||
|
"set_space_fit_sound",
|
||||||
|
cv.make_entity_service_schema({vol.Required("enabled"): bool}),
|
||||||
|
SmartThingsSoundbarMediaPlayer.async_set_space_fit_sound.__name__,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
domain_data = hass.data[DOMAIN]
|
domain_data = hass.data[DOMAIN]
|
||||||
|
|
||||||
|
addServices()
|
||||||
|
|
||||||
entities = []
|
entities = []
|
||||||
for key in domain_data.devices:
|
for key in domain_data.devices:
|
||||||
device_config: DeviceConfig = domain_data.devices[key]
|
device_config: DeviceConfig = domain_data.devices[key]
|
||||||
|
@ -171,9 +248,45 @@ class SmartThingsSoundbarMediaPlayer(MediaPlayerEntity):
|
||||||
async def async_media_pause(self):
|
async def async_media_pause(self):
|
||||||
await self.device.media_pause()
|
await self.device.media_pause()
|
||||||
|
|
||||||
|
async def async_media_next_track(self):
|
||||||
|
await self.device.media_next_track()
|
||||||
|
|
||||||
|
async def async_media_previous_track(self):
|
||||||
|
await self.device.media_previous_track()
|
||||||
|
|
||||||
async def async_media_stop(self):
|
async def async_media_stop(self):
|
||||||
await self.device.media_stop()
|
await self.device.media_stop()
|
||||||
|
|
||||||
|
# ---------- SERVICE_UTILITY ------------
|
||||||
|
|
||||||
|
async def async_set_woofer_level(self, level: int):
|
||||||
|
await self.device.set_woofer(level)
|
||||||
|
|
||||||
|
async def async_set_bass_mode(self, enabled: bool):
|
||||||
|
await self.device.set_bass_mode(enabled)
|
||||||
|
|
||||||
|
async def async_set_voice_mode(self, enabled: bool):
|
||||||
|
await self.device.set_voice_amplifier(enabled)
|
||||||
|
|
||||||
|
async def async_set_night_mode(self, enabled: bool):
|
||||||
|
await self.device.set_night_mode(enabled)
|
||||||
|
|
||||||
|
# ---------- SERVICE_UTILITY ------------
|
||||||
|
|
||||||
|
async def async_set_speaker_level(self, speaker_identifier: str, level: int):
|
||||||
|
await self.device.set_speaker_level(
|
||||||
|
SpeakerIdentifier(speaker_identifier), level
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_set_rear_speaker_mode(self, speaker_mode: str):
|
||||||
|
await self.device.set_rear_speaker_mode(RearSpeakerMode(speaker_mode))
|
||||||
|
|
||||||
|
async def async_set_active_voice_amplifier(self, enabled: bool):
|
||||||
|
await self.device.set_active_voice_amplifier(enabled)
|
||||||
|
|
||||||
|
async def async_set_space_fit_sound(self, enabled: bool):
|
||||||
|
await self.device.set_space_fit_sound(enabled)
|
||||||
|
|
||||||
# This property can be uncommented for some extra_attributes
|
# This property can be uncommented for some extra_attributes
|
||||||
# Still enabling this can cause side-effects.
|
# Still enabling this can cause side-effects.
|
||||||
# @property
|
# @property
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.number import (NumberEntity,
|
from homeassistant.components.number import (
|
||||||
|
NumberEntity,
|
||||||
NumberEntityDescription,
|
NumberEntityDescription,
|
||||||
NumberMode)
|
NumberMode,
|
||||||
|
)
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
|
||||||
from .api_extension.SoundbarDevice import SoundbarDevice
|
from .api_extension.SoundbarDevice import SoundbarDevice
|
||||||
from .const import CONF_ENTRY_DEVICE_ID, DOMAIN
|
from .const import CONF_ENTRY_DEVICE_ID, CONF_ENTRY_SETTINGS_WOOFER_NUMBER, DOMAIN
|
||||||
from .models import DeviceConfig
|
from .models import DeviceConfig
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -19,7 +21,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
for key in domain_data.devices:
|
for key in domain_data.devices:
|
||||||
device_config: DeviceConfig = domain_data.devices[key]
|
device_config: DeviceConfig = domain_data.devices[key]
|
||||||
device = device_config.device
|
device = device_config.device
|
||||||
if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID):
|
if device.device_id == config_entry.data.get(
|
||||||
|
CONF_ENTRY_DEVICE_ID
|
||||||
|
) and config_entry.data.get(CONF_ENTRY_SETTINGS_WOOFER_NUMBER):
|
||||||
entities.append(
|
entities.append(
|
||||||
SoundbarWooferNumberEntity(
|
SoundbarWooferNumberEntity(
|
||||||
device,
|
device,
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.number import (NumberEntity,
|
from homeassistant.components.number import (
|
||||||
|
NumberEntity,
|
||||||
NumberEntityDescription,
|
NumberEntityDescription,
|
||||||
NumberMode)
|
NumberMode,
|
||||||
from homeassistant.components.select import (SelectEntity,
|
)
|
||||||
SelectEntityDescription)
|
from homeassistant.components.select import SelectEntity, SelectEntityDescription
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
|
||||||
from .api_extension.SoundbarDevice import SoundbarDevice
|
from .api_extension.SoundbarDevice import SoundbarDevice
|
||||||
from .const import CONF_ENTRY_DEVICE_ID, DOMAIN
|
from .const import (
|
||||||
|
CONF_ENTRY_DEVICE_ID,
|
||||||
|
CONF_ENTRY_SETTINGS_EQ_SELECTOR,
|
||||||
|
CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
from .models import DeviceConfig
|
from .models import DeviceConfig
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -21,12 +27,17 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
device_config: DeviceConfig = domain_data.devices[key]
|
device_config: DeviceConfig = domain_data.devices[key]
|
||||||
device = device_config.device
|
device = device_config.device
|
||||||
if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID):
|
if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID):
|
||||||
|
if config_entry.data.get(CONF_ENTRY_SETTINGS_EQ_SELECTOR):
|
||||||
entities.append(
|
entities.append(
|
||||||
EqPresetSelectEntity(device, "eq_preset", "mdi:tune-vertical")
|
EqPresetSelectEntity(device, "eq_preset", "mdi:tune-vertical")
|
||||||
)
|
)
|
||||||
|
if config_entry.data.get(CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR):
|
||||||
entities.append(
|
entities.append(
|
||||||
SoundModeSelectEntity(device, "sound_mode_preset", "mdi:surround-sound")
|
SoundModeSelectEntity(
|
||||||
|
device, "sound_mode_preset", "mdi:surround-sound"
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
entities.append(
|
entities.append(
|
||||||
InputSelectEntity(device, "input_preset", "mdi:video-input-hdmi")
|
InputSelectEntity(device, "input_preset", "mdi:video-input-hdmi")
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from homeassistant.components.sensor import (SensorDeviceClass, SensorEntity,
|
from homeassistant.components.sensor import (
|
||||||
SensorStateClass)
|
SensorDeviceClass,
|
||||||
|
SensorEntity,
|
||||||
|
SensorStateClass,
|
||||||
|
)
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
|
||||||
from .api_extension.SoundbarDevice import SoundbarDevice
|
from .api_extension.SoundbarDevice import SoundbarDevice
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
|
||||||
|
select_soundmode:
|
||||||
|
name: Select Soundmode
|
||||||
|
description: Some Soundbars support different "sound modes". If supported you can select them here.
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
sound_mode:
|
||||||
|
name: Sound Mode
|
||||||
|
description: Select the Soundmode you are interested in.
|
||||||
|
required: true
|
||||||
|
example: "adaptive sound"
|
||||||
|
# The default field value
|
||||||
|
default: "standard"
|
||||||
|
# Selector (https://www.home-assistant.io/docs/blueprint/selectors/) to control
|
||||||
|
# the input UI for this field
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
translation_key: "soundmode"
|
||||||
|
options:
|
||||||
|
- "standard"
|
||||||
|
- "surround"
|
||||||
|
- "game"
|
||||||
|
- "adaptive sound"
|
||||||
|
|
||||||
|
set_woofer_level:
|
||||||
|
name: Set Woofer level
|
||||||
|
description: Set the subwoofer level of your soundbar
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
level:
|
||||||
|
name: Volume level
|
||||||
|
required: true
|
||||||
|
example: 3
|
||||||
|
default: 0
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: -12
|
||||||
|
max: 6
|
||||||
|
step: 1
|
||||||
|
|
||||||
|
set_night_mode:
|
||||||
|
name: Set NightMode
|
||||||
|
description: Activates / deactivates the Nightmode
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
enabled:
|
||||||
|
name: Enabled / Disabled
|
||||||
|
required: true
|
||||||
|
example: true
|
||||||
|
default: false
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
|
||||||
|
set_bass_enhancer:
|
||||||
|
name: Set bass enhancement
|
||||||
|
description: Activates / deactivates the bass enhancement
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
enabled:
|
||||||
|
name: Enabled / Disabled
|
||||||
|
required: true
|
||||||
|
example: true
|
||||||
|
default: false
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
|
||||||
|
set_voice_enhancer:
|
||||||
|
name: Set voice enhancement
|
||||||
|
description: Activates / deactivates the voice enhancement
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
enabled:
|
||||||
|
name: Enabled / Disabled
|
||||||
|
required: true
|
||||||
|
example: true
|
||||||
|
default: false
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
|
||||||
|
set_speaker_level:
|
||||||
|
name: Set Speaker level
|
||||||
|
description: Set the speaker levels of your soundbar
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
speaker_identifier:
|
||||||
|
name: Speaker Identifier
|
||||||
|
required: true
|
||||||
|
example: Spk_Center
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
translation_key: "speaker_identifier"
|
||||||
|
options:
|
||||||
|
- "Spk_Center"
|
||||||
|
- "Spk_Side"
|
||||||
|
- "Spk_Wide"
|
||||||
|
- "Spk_Front_Top"
|
||||||
|
- "Spk_Rear"
|
||||||
|
- "Spk_Rear_Top"
|
||||||
|
level:
|
||||||
|
name: Speaker Level
|
||||||
|
required: true
|
||||||
|
example: 0
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: -6
|
||||||
|
max: 6
|
||||||
|
step: 1
|
||||||
|
|
||||||
|
set_rear_speaker_mode:
|
||||||
|
name: Set rear speaker mode
|
||||||
|
description: Set the rear speaker mode of your soundbar
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
speaker_mode:
|
||||||
|
name: Speaker mode
|
||||||
|
required: true
|
||||||
|
example: Rear
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
translation_key: "rear_speaker_mode"
|
||||||
|
options:
|
||||||
|
- "Rear"
|
||||||
|
- "Front"
|
||||||
|
|
||||||
|
set_active_voice_amplifier:
|
||||||
|
name: Set active voice amplifier
|
||||||
|
description: Activates / deactivates the active voice amplifier
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
enabled:
|
||||||
|
name: Enabled / Disabled
|
||||||
|
required: true
|
||||||
|
example: true
|
||||||
|
default: false
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
|
||||||
|
set_space_fit_sound:
|
||||||
|
name: Set SpaceFitSound
|
||||||
|
description: Activates / deactivates the SpaceFitSound
|
||||||
|
target:
|
||||||
|
device:
|
||||||
|
integration: samsung_soundbar
|
||||||
|
fields:
|
||||||
|
enabled:
|
||||||
|
name: Enabled / Disabled
|
||||||
|
required: true
|
||||||
|
example: true
|
||||||
|
default: false
|
||||||
|
selector:
|
||||||
|
boolean:
|
|
@ -4,7 +4,11 @@ from homeassistant.components.switch import SwitchEntity
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
|
||||||
from .api_extension.SoundbarDevice import SoundbarDevice
|
from .api_extension.SoundbarDevice import SoundbarDevice
|
||||||
from .const import CONF_ENTRY_DEVICE_ID, DOMAIN
|
from .const import (
|
||||||
|
CONF_ENTRY_DEVICE_ID,
|
||||||
|
CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
from .models import DeviceConfig
|
from .models import DeviceConfig
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -18,6 +22,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||||
device_config: DeviceConfig = domain_data.devices[key]
|
device_config: DeviceConfig = domain_data.devices[key]
|
||||||
device = device_config.device
|
device = device_config.device
|
||||||
if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID):
|
if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID):
|
||||||
|
if config_entry.data.get(CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES):
|
||||||
entities.append(
|
entities.append(
|
||||||
SoundbarSwitchAdvancedAudio(
|
SoundbarSwitchAdvancedAudio(
|
||||||
device,
|
device,
|
||||||
|
|
|
@ -10,6 +10,143 @@
|
||||||
},
|
},
|
||||||
"description": "Bitte gib deine Daten ein.",
|
"description": "Bitte gib deine Daten ein.",
|
||||||
"title": "Authentifizierung"
|
"title": "Authentifizierung"
|
||||||
|
},
|
||||||
|
"device":{
|
||||||
|
"data" : {
|
||||||
|
"settings_advanced_audio": "'Advanced Audio switches' aktivieren (NightMode, BassMode, VoiceEnhancer)",
|
||||||
|
"settings_eq": "'EQ selector' aktivieren",
|
||||||
|
"settings_soundmode": "'Soundmode selector' aktivieren",
|
||||||
|
"settings_woofer": "'Subwoofer Entität' aktivieren"
|
||||||
|
},
|
||||||
|
"description": "Einige Soundbars haben verschiedene Featuresets. Wähle bitte aus welche Features von deiner Soundbar supported werden (einsehbar in der SmartThings App).",
|
||||||
|
"title": "Geräte Einstellungen"
|
||||||
|
},
|
||||||
|
"reconfigure_confirm":{
|
||||||
|
"data" : {
|
||||||
|
"settings_advanced_audio": "'Advanced Audio switches' aktivieren (NightMode, BassMode, VoiceEnhancer)",
|
||||||
|
"settings_eq": "'EQ selector' aktivieren",
|
||||||
|
"settings_soundmode": "'Soundmode selector' aktivieren",
|
||||||
|
"settings_woofer": "'Subwoofer Entität' aktivieren",
|
||||||
|
"device_volume": "Max Volume (int)"
|
||||||
|
},
|
||||||
|
"description": "Einige Soundbars haben verschiedene Featuresets. Wähle bitte aus welche Features von deiner Soundbar supported werden (einsehbar in der SmartThings App).",
|
||||||
|
"title": "Geräte Einstellungen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selector": {
|
||||||
|
"soundmode": {
|
||||||
|
"options": {
|
||||||
|
"standard": "Standard",
|
||||||
|
"surround": "Surround",
|
||||||
|
"game": "Gaming",
|
||||||
|
"adaptive sound": "Adaptive Sound"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"speaker_identifier": {
|
||||||
|
"options": {
|
||||||
|
"Spk_Center": "Center",
|
||||||
|
"Spk_Side": "Side",
|
||||||
|
"Spk_Wide": "Wide",
|
||||||
|
"Spk_Front_Top": "Front Top",
|
||||||
|
"Spk_Rear": "Rear",
|
||||||
|
"Spk_Rear_Top": "Rear Top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rear_speaker_mode": {
|
||||||
|
"options": {
|
||||||
|
"Rear": "Rear",
|
||||||
|
"Front": "Front"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services":{
|
||||||
|
"select_soundmode":{
|
||||||
|
"name": "SoundMode auswählen",
|
||||||
|
"description": "Wähle hier zwischen, 'Standard', 'Surround', 'Game' und 'Adaptive Sound'."
|
||||||
|
},
|
||||||
|
"set_woofer_level":{
|
||||||
|
"name": "Subwoofer Level setzen",
|
||||||
|
"description": "Verändere die Lautstärke deines Subwoofers.",
|
||||||
|
"fields":{
|
||||||
|
"level":{
|
||||||
|
"name": "Volume Level",
|
||||||
|
"description": "Subwoofer Level, von -12 bis +6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_night_mode":{
|
||||||
|
"name": "Nachtmodus setzen",
|
||||||
|
"description": "Schalte den 'Nachtmodus' an / aus.",
|
||||||
|
"fields":{
|
||||||
|
"enabled":{
|
||||||
|
"name": "An / ausschalten",
|
||||||
|
"description": "Siehe Name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_bass_enhancer":{
|
||||||
|
"name": "Bassmodus setzen",
|
||||||
|
"description": "Schalte den 'Bassmodus' an / aus.",
|
||||||
|
"fields":{
|
||||||
|
"enabled":{
|
||||||
|
"name": "An / ausschalten",
|
||||||
|
"description": "Siehe Name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_voice_enhancer":{
|
||||||
|
"name": "Stimmenverbesserer setzen",
|
||||||
|
"description": "Schalte den 'Stimmenverbesserer' an / aus.",
|
||||||
|
"fields":{
|
||||||
|
"enabled":{
|
||||||
|
"name": "An / ausschalten",
|
||||||
|
"description": "Siehe Name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_speaker_level":{
|
||||||
|
"name": "Lautsprecher level verändern",
|
||||||
|
"description": "Verändere die Lautstärke der einzelnen Lautsprecher",
|
||||||
|
"fields":{
|
||||||
|
"speaker_identifier": {
|
||||||
|
"name": "Lautsprecher",
|
||||||
|
"description": "Auszuwählender Lautsprecher"
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"name": "Lautstärke Level",
|
||||||
|
"description": "Lautstärke Level zwischen -6 und 6."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_rear_speaker_mode":{
|
||||||
|
"name": "Modus der hinteren Lautsprecher setzen",
|
||||||
|
"description": "Nutze deine Rücklautsprecher, als 'Vorder-' oder 'Rücklautsprecher'.",
|
||||||
|
"fields":{
|
||||||
|
"speaker_mode": {
|
||||||
|
"name": "Lautsprecher Modus",
|
||||||
|
"description": "Nutze den Lautsprecher als Front oder Rear Speaker."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_active_voice_amplifier":{
|
||||||
|
"name": "Stimmenverstärker setzen",
|
||||||
|
"description": "Schalte den 'Stimmenverstärker' an / aus.",
|
||||||
|
"fields":{
|
||||||
|
"enabled":{
|
||||||
|
"name": "An / ausschalten",
|
||||||
|
"description": "Siehe Name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_space_fit_sound":{
|
||||||
|
"name": "SpaceFitSound setzen",
|
||||||
|
"description": "Schalte den 'SpaceFitSound' an / aus.",
|
||||||
|
"fields":{
|
||||||
|
"enabled":{
|
||||||
|
"name": "An / ausschalten",
|
||||||
|
"description": "Siehe Name."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,143 @@
|
||||||
},
|
},
|
||||||
"description": "Please enter your credentials.",
|
"description": "Please enter your credentials.",
|
||||||
"title": "Authentication"
|
"title": "Authentication"
|
||||||
|
},
|
||||||
|
"device": {
|
||||||
|
"data": {
|
||||||
|
"settings_advanced_audio": "Enable 'Advanced Audio switches' capabilities (NightMode, BassMode, VoiceEnhancer)",
|
||||||
|
"settings_eq": "Enable 'EQ selector' capabilities",
|
||||||
|
"settings_soundmode": "Enable 'Soundmode selector' capabilities",
|
||||||
|
"settings_woofer": "Enable 'Woofer number' capability"
|
||||||
|
},
|
||||||
|
"description": "Some soundbars have a different featureset than others. Please the features supported by your soundbar (visible in the SmartThings App).",
|
||||||
|
"title": "Device Settings"
|
||||||
|
},
|
||||||
|
"reconfigure_confirm": {
|
||||||
|
"data": {
|
||||||
|
"settings_advanced_audio": "Enable 'Advanced Audio switches' capabilities (NightMode, BassMode, VoiceEnhancer)",
|
||||||
|
"settings_eq": "Enable 'EQ selector' capabilities",
|
||||||
|
"settings_soundmode": "Enable 'Soundmode selector' capabilities",
|
||||||
|
"settings_woofer": "Enable 'Woofer number' capability",
|
||||||
|
"device_volume": "Max Volume (int)"
|
||||||
|
},
|
||||||
|
"description": "Some soundbars have a different featureset than others. Please the features supported by your soundbar (visible in the SmartThings App).",
|
||||||
|
"title": "Device Settings"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"selector": {
|
||||||
|
"soundmode": {
|
||||||
|
"options": {
|
||||||
|
"standard": "Standard",
|
||||||
|
"surround": "Surround",
|
||||||
|
"game": "Gaming",
|
||||||
|
"adaptive sound": "Adaptive Sound"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"speaker_identifier": {
|
||||||
|
"options": {
|
||||||
|
"Spk_Center": "Center",
|
||||||
|
"Spk_Side": "Side",
|
||||||
|
"Spk_Wide": "Wide",
|
||||||
|
"Spk_Front_Top": "Front Top",
|
||||||
|
"Spk_Rear": "Rear",
|
||||||
|
"Spk_Rear_Top": "Rear Top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rear_speaker_mode": {
|
||||||
|
"options": {
|
||||||
|
"Rear": "Rear",
|
||||||
|
"Front": "Front"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"select_soundmode": {
|
||||||
|
"name": "Select Sound Mode",
|
||||||
|
"description": "Choose between 'Standard', 'Surround', 'Game', and 'Adaptive Sound'."
|
||||||
|
},
|
||||||
|
"set_woofer_level": {
|
||||||
|
"name": "Set Subwoofer Level",
|
||||||
|
"description": "Change the volume of your subwoofer.",
|
||||||
|
"fields": {
|
||||||
|
"level": {
|
||||||
|
"name": "Volume Level",
|
||||||
|
"description": "Subwoofer level, from -12 to +6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_night_mode": {
|
||||||
|
"name": "Set Night Mode",
|
||||||
|
"description": "Turn 'Night Mode' on/off.",
|
||||||
|
"fields": {
|
||||||
|
"enabled": {
|
||||||
|
"name": "On/Off",
|
||||||
|
"description": "See name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_bass_enhancer": {
|
||||||
|
"name": "Set Bass Mode",
|
||||||
|
"description": "Turn 'Bass Mode' on/off.",
|
||||||
|
"fields": {
|
||||||
|
"enabled": {
|
||||||
|
"name": "On/Off",
|
||||||
|
"description": "See name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_voice_enhancer": {
|
||||||
|
"name": "Set Voice Enhancer",
|
||||||
|
"description": "Turn 'Voice Enhancer' on/off.",
|
||||||
|
"fields": {
|
||||||
|
"enabled": {
|
||||||
|
"name": "On/Off",
|
||||||
|
"description": "See name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_speaker_level": {
|
||||||
|
"name": "Change Speaker Level",
|
||||||
|
"description": "Change the volume of individual speakers.",
|
||||||
|
"fields":{
|
||||||
|
"speaker_identifier": {
|
||||||
|
"name": "Speaker Identifier",
|
||||||
|
"description": "Identifier of the speaker."
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"name": "Level",
|
||||||
|
"description": "Level of the Speaker from -6 to 6."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_rear_speaker_mode": {
|
||||||
|
"name": "Set Rear Speaker Mode",
|
||||||
|
"description": "Use your rear speakers as 'Front' or 'Rear' speakers.",
|
||||||
|
"fields":{
|
||||||
|
"speaker_mode": {
|
||||||
|
"name": "Speaker mode",
|
||||||
|
"description": "Weather the speaker are used as rear / front speakers."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_active_voice_amplifier": {
|
||||||
|
"name": "Set Voice Amplifier",
|
||||||
|
"description": "Turn 'Voice Amplifier' on/off.",
|
||||||
|
"fields": {
|
||||||
|
"enabled": {
|
||||||
|
"name": "On/Off",
|
||||||
|
"description": "See name."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"set_space_fit_sound": {
|
||||||
|
"name": "Set SpaceFitSound",
|
||||||
|
"description": "Turn 'SpaceFitSound' on/off.",
|
||||||
|
"fields": {
|
||||||
|
"enabled": {
|
||||||
|
"name": "On/Off",
|
||||||
|
"description": "See name."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export default defineAppConfig({
|
export default defineAppConfig({
|
||||||
docus: {
|
docus: {
|
||||||
title: 'YASSI',
|
title: '🔊 Yassi',
|
||||||
description: 'HomeAssistant: Yet another Samsung soundbar integration',
|
description: 'Yet another Samsung Soundbar integration for Home Assistant',
|
||||||
image: 'https://user-images.githubusercontent.com/904724/185365452-87b7ca7b-6030-4813-a2db-5e65c785bf88.png',
|
image: 'https://user-images.githubusercontent.com/904724/185365452-87b7ca7b-6030-4813-a2db-5e65c785bf88.png',
|
||||||
socials: {
|
socials: {
|
||||||
github: 'samuelspagl/ha_samsung_soundbar',
|
github: 'samuelspagl/ha_samsung_soundbar',
|
||||||
|
@ -28,7 +28,7 @@ export default defineAppConfig({
|
||||||
fluid: true
|
fluid: true
|
||||||
},
|
},
|
||||||
header: {
|
header: {
|
||||||
logo: true,
|
logo: false,
|
||||||
showLinkIcon: true,
|
showLinkIcon: true,
|
||||||
exclude: [],
|
exclude: [],
|
||||||
fluid: true
|
fluid: true
|
||||||
|
|
|
@ -5,24 +5,19 @@ title: "YASSI"
|
||||||
---
|
---
|
||||||
cta:
|
cta:
|
||||||
- Why another HomeAssistant integration?
|
- Why another HomeAssistant integration?
|
||||||
- #why-another-integration
|
- /first-things-first/why-another-integration
|
||||||
secondary:
|
secondary:
|
||||||
- Open on GitHub →
|
- Open on GitHub →
|
||||||
- https://github.com/nuxtlabs/docus
|
- https://github.com/samuelspagl/ha_samsung_soundbar
|
||||||
snippet:
|
|
||||||
- Custom Components
|
|
||||||
- "- input selection"
|
|
||||||
- "- soundmode selection"
|
|
||||||
- "- eq-preset selection"
|
|
||||||
- "- woofer settings"
|
|
||||||
- "- other cool things"
|
|
||||||
---
|
---
|
||||||
|
|
||||||
#title
|
#title
|
||||||
Yassi
|
Yassi - Yet another Samsung Soundbar integration
|
||||||
|
|
||||||
#description
|
#description
|
||||||
Yet another Samsung soundbar integration for HomeAssistant
|
**YASSI** is a **HomeAssistant** integration for **Samsung Soundbars**. It enhances control, and adds features like equalizer settings. Install it via HACS or manually. Kudos to the original idea by @PiotrMachowski and @thierryBourbon! 🎶🔊
|
||||||
|
|
||||||
|
[](https://my.home-assistant.io/redirect/hacs_repository/?owner=samuelspagl&repository=ha_samsung_soundbar&category=integration)
|
||||||
::
|
::
|
||||||
|
|
||||||
::card-grid
|
::card-grid
|
||||||
|
@ -34,36 +29,42 @@ Quick-Start
|
||||||
#default
|
#default
|
||||||
::card
|
::card
|
||||||
#title
|
#title
|
||||||
Getting Started.
|
❓ Why another integration?
|
||||||
#description
|
#description
|
||||||
Go, Go, Go... Here you will find information on "How to install / configure".
|
Whether you thought about it or not, here is the answer ;).
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
:button-link[click here]{href="/getting-started"}
|
:button-link[click here]{href="/first-things-first/why-another-integration"}
|
||||||
::
|
::
|
||||||
|
|
||||||
::card
|
::card
|
||||||
#title
|
#title
|
||||||
Features
|
🚀 Getting Started
|
||||||
|
#description
|
||||||
|
Go, Go, Go... Here you will find information on "How to install / configure".
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
:button-link[click here]{href="/first-things-first/getting-started"}
|
||||||
|
::
|
||||||
|
|
||||||
|
::card
|
||||||
|
#title
|
||||||
|
✨ Features
|
||||||
#description
|
#description
|
||||||
Many cool features are awaiting your eyes to see ✨.
|
Many cool features are awaiting your eyes to see ✨.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
:button-link[click here]{href="/features"}
|
:button-link[click here]{href="/features"}
|
||||||
::
|
::
|
||||||
|
|
||||||
|
::card
|
||||||
|
#title
|
||||||
|
⚙️ SmartThings API related information
|
||||||
|
#description
|
||||||
|
If you want to know some background information on how equalizer support and
|
||||||
|
other things were implemented, this is your section.
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
:button-link[click here]{href="/features"}
|
||||||
|
::
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|
||||||
## Why another integration
|
|
||||||
|
|
||||||
The current Samsung Soundbar Integration by @PiotrMachowski / @thierryBourbon are already pretty cool.
|
|
||||||
But I wanted it to appear as a device, and base the Foundation on the `pysmartthings` python package.
|
|
||||||
|
|
||||||
Additionally, I wanted full control over the *Soundmode* and more. So I tried out a few things with the API,
|
|
||||||
and found that also the **Subwoofer** as well as the **Equalizer** are controllable.
|
|
||||||
|
|
||||||
I created a new wrapper around the `pysmartthings.DeviceEntity` specifically set up for a Soundbar, and this
|
|
||||||
is the Result.
|
|
||||||
|
|
||||||
I hope to integrate also controls for **surround speaker** as well as **Space-Fit Sound**, but as these features
|
|
||||||
are not documented... ;)
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# ❓ Why another integration
|
||||||
|
|
||||||
|
The current Samsung Soundbar Integration by @PiotrMachowski / @thierryBourbon are already pretty cool.
|
||||||
|
But I wanted it to appear as a device, and base the Foundation on the `pysmartthings` python package.
|
||||||
|
|
||||||
|
Additionally, I wanted full control over the *Soundmode* and more. So I tried out a few things with the API,
|
||||||
|
and found that also the **Subwoofer** as well as the **Equalizer** are controllable.
|
||||||
|
|
||||||
|
I created a new wrapper around the `pysmartthings.DeviceEntity` specifically set up for a Soundbar, and this
|
||||||
|
is the Result.
|
||||||
|
|
||||||
|
I hope to integrate also controls for **surround speaker** as well as **Space-Fit Sound**, but as these features
|
||||||
|
are not documented... ;)
|
|
@ -0,0 +1,43 @@
|
||||||
|
# 🚀 Getting Started with Yassi
|
||||||
|
|
||||||
|
Welcome to Yassi, the HomeAssistant integration for your Samsung Soundbar. This guide will help you get up and running in no time.
|
||||||
|
|
||||||
|
## 📦 Installation Options
|
||||||
|
|
||||||
|
### HACS (Home Assistant Community Store)
|
||||||
|
|
||||||
|
#### 🌟 Official Repository (Coming Soon)
|
||||||
|
The Yassi integration will be available through the official HACS repository shortly. Stay tuned for updates.
|
||||||
|
|
||||||
|
#### ➕ Custom Repository
|
||||||
|
In the meantime, you can manually add this repository to HACS:
|
||||||
|
1. Click the following button and 'open link':
|
||||||
|
[](https://my.home-assistant.io/redirect/hacs_repository/?owner=samuelspagl&repository=ha_samsung_soundbar&category=integration)
|
||||||
|
2. Click 'add' to add the custom repository.
|
||||||
|
3. Download 'Yassi' and restart Home Assistant.
|
||||||
|
|
||||||
|
### 📂 Manual Installation
|
||||||
|
If you prefer to install Yassi manually:
|
||||||
|
1. Download the latest release from the repository.
|
||||||
|
2. Extract and copy the `custom_components/samsung_soundbar` folder.
|
||||||
|
3. Paste it into the `config/custom_components/samsung_soundbar` directory of your HomeAssistant setup.
|
||||||
|
|
||||||
|
## ⚙️ Configuration Steps
|
||||||
|
|
||||||
|
Once Yassi is installed, you can configure it via the HomeAssistant UI:
|
||||||
|
|
||||||
|
1. Go to 'Configuration' and then 'Integrations'.
|
||||||
|
2. Click on 'Add Integration' and search for 'Yassi'.
|
||||||
|
3. Enter the following details to complete the setup:
|
||||||
|
- 🔑 SmartThings API Key: [Obtain it here](https://account.smartthings.com/tokens).
|
||||||
|
- 🆔 Device ID: [Find your Soundbar's device ID here](https://my.smartthings.com/advanced/devices).
|
||||||
|
- ㍻ Soundbar Name: Choose a name for easy identification.
|
||||||
|
- 🔊 Max Volume: Set the maximum volume limit for your Soundbar.
|
||||||
|
|
||||||
|
Follow these steps, and you'll be enjoying seamless control over your Samsung Soundbar with Yassi in no time!
|
||||||
|
|
||||||
|
::alert{type="info"}
|
||||||
|
The `🔊 Max Volume` setting will readjust the internal values of the `media_player` entity from 0-100 to 0-MaxVolume.
|
||||||
|
Therefore will the slider not display the same value as the one provided by the `sensor` entity, which will always display
|
||||||
|
the raw value retrieved from the SmartThings API.
|
||||||
|
::
|
|
@ -0,0 +1,17 @@
|
||||||
|
# ‼️ Issues and other things
|
||||||
|
|
||||||
|
As the creator of this personal and fun project, I am thrilled to see people using it. While I won’t always have immediate availability to address every request, I’ll do my best to fix issues and implement features. Thanks a lot in advance! 🙌
|
||||||
|
|
||||||
|
Here are some best practices to help me help you:
|
||||||
|
|
||||||
|
1. 🐞 GitHub Issues: For any issues or bugs, please submit them via GitHub Issues. ([🔗 click here](https://github.com/samuelspagl/ha_samsung_soundbar/issues/new))
|
||||||
|
2. 📋 Provide Details: Include essential information:
|
||||||
|
- Home Assistant OS Version
|
||||||
|
- Samsung Soundbar Model
|
||||||
|
- Other Relevant Details (like debug logs)
|
||||||
|
3. 🎇 Icons for Fun:
|
||||||
|
- 📦 = Feature Request
|
||||||
|
- 🐛 = Bug Report
|
||||||
|
- ❓ = General Questions
|
||||||
|
|
||||||
|
Let’s collaborate to enhance your soundbar experience! 🎶🔊
|
|
@ -1,30 +0,0 @@
|
||||||
# Getting Started
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### HACS (official)
|
|
||||||
|
|
||||||
> ⚠️ Not done yet, hopefully soon.
|
|
||||||
|
|
||||||
|
|
||||||
### HACS (custom repository)
|
|
||||||
|
|
||||||
You can add this repository as a custom repository to your hacs.
|
|
||||||
After you've done that, you can search for it like with the "official"
|
|
||||||
integrations.
|
|
||||||
|
|
||||||
### Manual
|
|
||||||
|
|
||||||
Copy the contents of `custom_components/samsung_soundbar` to `config/custom_components/samsung_soundbar`
|
|
||||||
on your HomeAssistant instance.
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
After you installed the custom component, it should be possible to configure the integration
|
|
||||||
in the `device` settings of your HomeAssistant.
|
|
||||||
|
|
||||||
You will need:
|
|
||||||
- a SmartThings `api_key` [click here](https://account.smartthings.com/tokens)
|
|
||||||
- the `device_id` of your device [click here](https://my.smartthings.com/advanced/devices)
|
|
||||||
- a name for your Soundbar
|
|
||||||
- and a `max_volume`
|
|
|
@ -1,35 +1,52 @@
|
||||||
# Features
|
# ✨ Features Overview
|
||||||
|
|
||||||
**YASSI** and retrieve / set the status of the following features grouped as a device:
|
Yassi allows you to retrieve and set the status of various features on your Samsung Soundbar. Below is a breakdown of capabilities organized by entity type.
|
||||||
- `media_player`:
|
|
||||||
- `on / off` [*read, write*]
|
|
||||||
- `volume` (set, step) [*read, write*]
|
|
||||||
- `input` (select) [*read*, write*]
|
|
||||||
- `sound_mode` (select) [*read, write*]
|
|
||||||
- `play` (button) [*write*]
|
|
||||||
- `pause` (button) [*write*]
|
|
||||||
- `media_artwork` (image) [*read*]
|
|
||||||
- `media_title` (text) [*read*]
|
|
||||||
- `media_artist` (text) [*read*]
|
|
||||||
|
|
||||||
- `number`
|
## `media_player` Entity
|
||||||
- **Woofer**
|
|
||||||
- level (set) [*read, write*]
|
|
||||||
- `select`
|
|
||||||
- **Input**
|
|
||||||
- `input` [*read, write*]
|
|
||||||
- `supported_inputs` [*read*]
|
|
||||||
- **Soundmode**
|
|
||||||
- `active_soundmode` [*read, write*]
|
|
||||||
- `supported_soundmodes` [*read*]
|
|
||||||
- **EQ-Preset**
|
|
||||||
- `active_eq_preset` [*read, write*]
|
|
||||||
- `supported_eq_preset` [*read*]
|
|
||||||
|
|
||||||
- `button`
|
| **Feature** | **Capability** | **Access Type** |
|
||||||
- `night_mode` [*read, write*]
|
|-------------------|----------------|-----------------|
|
||||||
- `voice_amplifier` [*read, write*]
|
| Power | on / off | Read, Write |
|
||||||
- `bass_mode` [*read, write*]
|
| Volume | set, step | Read, Write |
|
||||||
|
| Input Selection | select | Read, Write |
|
||||||
|
| Sound Mode | select | Read, Write |
|
||||||
|
| Playback Control | play, pause | Write |
|
||||||
|
| Media Information | artwork, title, artist | Read |
|
||||||
|
|
||||||
- `image`
|
## `number` Entity
|
||||||
- `media_coverart` [*read*]
|
|
||||||
|
| **Feature** | **Capability** | **Access Type** |
|
||||||
|
|-------------|----------------|-----------------|
|
||||||
|
| Woofer Level | set | Read, Write |
|
||||||
|
|
||||||
|
## `select` Entity
|
||||||
|
|
||||||
|
| **Feature** | **Capability** | **Access Type** |
|
||||||
|
|-------------------|-----------------------|-----------------|
|
||||||
|
| Input | input, supported_inputs | Read, Write |
|
||||||
|
| Sound Mode | active_soundmode, supported_soundmodes | Read, Write |
|
||||||
|
| EQ-Preset | active_eq_preset, supported_eq_preset | Read, Write |
|
||||||
|
|
||||||
|
## `button` Entity
|
||||||
|
|
||||||
|
| **Feature** | **Capability** | **Access Type** |
|
||||||
|
|-------------------|----------------|-----------------|
|
||||||
|
| Night Mode | toggle | Read, Write |
|
||||||
|
| Voice Amplifier | toggle | Read, Write |
|
||||||
|
| Bass Mode | toggle | Read, Write |
|
||||||
|
|
||||||
|
## `image` Entity
|
||||||
|
|
||||||
|
| **Feature** | **Capability** | **Access Type** |
|
||||||
|
|-------------------|----------------|-----------------|
|
||||||
|
| Media Cover Art | display | Read |
|
||||||
|
|
||||||
|
|
||||||
|
## `sensor` Entity
|
||||||
|
|
||||||
|
| **Feature** | **Capability** | **Access Type** |
|
||||||
|
|-------------|--------------------|-----------------|
|
||||||
|
| Volume | float sensor value | Read |
|
||||||
|
|
||||||
|
|
||||||
|
Hopefully this format provides a clear and concise view of what Yassi can do with your Samsung Soundbar, making it easier to understand and configure.
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
"filename": "samsung_soundbar.zip",
|
"filename": "samsung_soundbar.zip",
|
||||||
"render_readme": true,
|
"render_readme": true,
|
||||||
"zip_release": true,
|
"zip_release": true,
|
||||||
"homeassistant": "2024.1.0"
|
"homeassistant": "2024.3.0"
|
||||||
}
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
# Create config dir if not present
|
||||||
|
if [[ ! -d "${PWD}/config" ]]; then
|
||||||
|
mkdir -p "${PWD}/config"
|
||||||
|
hass --config "${PWD}/config" --script ensure_config
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the path to custom_components
|
||||||
|
## This let's us have the structure we want <root>/custom_components/integration_blueprint
|
||||||
|
## while at the same time have Home Assistant configuration inside <root>/config
|
||||||
|
## without resulting to symlinks.
|
||||||
|
export PYTHONPATH="${PYTHONPATH}:${PWD}/custom_components"
|
||||||
|
|
||||||
|
# Start Home Assistant
|
||||||
|
hass --config "${PWD}/config" --debug
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
pip install rich pysmartthings
|
Loading…
Reference in New Issue