Compare commits

...

6 Commits

Author SHA1 Message Date
orkun c113688986 Update custom_components/samsung_soundbar/manifest.json 2025-04-10 19:37:48 +03:00
orkun 8cd2aa3d51 Update custom_components/samsung_soundbar/manifest.json 2025-04-10 19:36:41 +03:00
Samuel Spagl bd313ea27a
Fix 'audiotrackdata' (#42)
## [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
2024-10-14 21:46:14 +02:00
Samuel Spagl 0d2424b578
Feature: Add more fine grained configuration steps (#28)
> ⚠️ 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

---------

Co-authored-by: Samuel Spagl <samuel.spagl@kobil.com>
2024-06-09 17:13:38 +02:00
Samuel Spagl 9bc8be7861
Add MIT LICENSE 2024-04-08 21:53:29 +02:00
Samuel Spagl 14e30ba970
Update README.md
Add note about current API issues
2024-04-05 16:58:44 +02:00
24 changed files with 1566 additions and 549 deletions

View File

@ -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"
}

5
.gitignore vendored
View File

@ -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

View File

@ -1,5 +1,46 @@
# 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 ## [0.3.2] Fix division by zero
### Added ### Added

21
LICENSE Normal file
View File

@ -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.

View File

@ -9,8 +9,7 @@ rich = "*"
homeassistant = "*" homeassistant = "*"
[dev-packages] [dev-packages]
black = "*" ruff = "*"
isort = "*"
[requires] [requires]
python_version = "3.11" python_version = "3.12"

972
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,9 @@
Welcome to YASSI, the Home Assistant integration designed to bring comprehensive control over your Samsung Soundbar into your smart home ecosystem. 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:** **Table of Contents:**
<!-- TOC --> <!-- TOC -->
* [Why YASSI](#why-yassi) * [Why YASSI](#why-yassi)
@ -81,4 +84,4 @@ Contributions are what make the open-source community such an amazing place to l
## General Thanks ## General Thanks
- Like already mentioned, thanks to @PiotrMachowski / @thierryBourbon for the general idea on how to do things. - Like already mentioned, thanks to @PiotrMachowski / @thierryBourbon for the general idea on how to do things.

View File

@ -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(

View File

@ -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,35 +61,41 @@ class SoundbarDevice:
await self.device.status.refresh() await self.device.status.refresh()
await self._update_media() await self._update_media()
await self._update_soundmode()
await self._update_advanced_audio() if self.__enable_soundmode:
await self._update_woofer() await self._update_soundmode()
await self._update_equalizer() if self.__enable_advanced_audio:
await self._update_advanced_audio()
if self.__enable_soundmode:
await self._update_woofer()
if self.__enable_eq:
await self._update_equalizer()
async def _update_media(self): async def _update_media(self):
self.__media_artist = self.device.status._attributes["audioTrackData"].value[ if "audioTrackData" in self.device.status._attributes:
"artist" self.__media_artist = self.device.status._attributes["audioTrackData"].value[
] "artist"
self.__media_title = self.device.status._attributes["audioTrackData"].value[ ]
"title" self.__media_title = self.device.status._attributes["audioTrackData"].value[
] "title"
if self.__media_title != self.__old_media_title: ]
self.__old_media_title = self.__media_title if self.__media_title != self.__old_media_title:
self.__media_cover_url_update_time = datetime.datetime.now() self.__old_media_title = self.__media_title
self.__media_cover_url = await self.get_song_title_artwork( self.__media_cover_url_update_time = datetime.datetime.now()
self.__media_artist, self.__media_title self.__media_cover_url = await self.get_song_title_artwork(
) self.__media_artist, self.__media_title
)
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:
""" """

View File

@ -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"

View File

@ -1,4 +1,5 @@
import logging import logging
from typing import Any
import pysmartthings import pysmartthings
import voluptuous as vol import voluptuous as vol
@ -7,8 +8,17 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
from pysmartthings import APIResponseError from pysmartthings import APIResponseError
from voluptuous import All, Range 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__)
@ -24,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",
@ -46,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, default=100): All(int, Range(min=1, max=100)) 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,
)

View File

@ -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

View File

@ -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.2" "pysmartthings==0.7.8"
} ],
"version": "0.4.1"
}

View File

@ -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

View File

@ -1,12 +1,14 @@
import logging import logging
from homeassistant.components.number import (NumberEntity, from homeassistant.components.number import (
NumberEntityDescription, NumberEntity,
NumberMode) NumberEntityDescription,
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,

View File

@ -1,14 +1,20 @@
import logging import logging
from homeassistant.components.number import (NumberEntity, from homeassistant.components.number import (
NumberEntityDescription, NumberEntity,
NumberMode) NumberEntityDescription,
from homeassistant.components.select import (SelectEntity, NumberMode,
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):
entities.append( if config_entry.data.get(CONF_ENTRY_SETTINGS_EQ_SELECTOR):
EqPresetSelectEntity(device, "eq_preset", "mdi:tune-vertical") entities.append(
) EqPresetSelectEntity(device, "eq_preset", "mdi:tune-vertical")
entities.append( )
SoundModeSelectEntity(device, "sound_mode_preset", "mdi:surround-sound") if config_entry.data.get(CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR):
) entities.append(
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")
) )

View File

@ -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

View File

@ -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:

View File

@ -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,36 +22,37 @@ 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):
entities.append( if config_entry.data.get(CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES):
SoundbarSwitchAdvancedAudio( entities.append(
device, SoundbarSwitchAdvancedAudio(
"nightmode", device,
lambda: device.night_mode, "nightmode",
device.set_night_mode, lambda: device.night_mode,
device.set_night_mode, device.set_night_mode,
"mdi:weather-night", device.set_night_mode,
"mdi:weather-night",
)
) )
) entities.append(
entities.append( SoundbarSwitchAdvancedAudio(
SoundbarSwitchAdvancedAudio( device,
device, "bassmode",
"bassmode", lambda: device.bass_mode,
lambda: device.bass_mode, device.set_bass_mode,
device.set_bass_mode, device.set_bass_mode,
device.set_bass_mode, "mdi:speaker-wireless",
"mdi:speaker-wireless", )
) )
) entities.append(
entities.append( SoundbarSwitchAdvancedAudio(
SoundbarSwitchAdvancedAudio( device,
device, "voice_amplifier",
"voice_amplifier", lambda: device.voice_amplifier,
lambda: device.voice_amplifier, device.set_voice_amplifier,
device.set_voice_amplifier, device.set_voice_amplifier,
device.set_voice_amplifier, "mdi:account-voice",
"mdi:account-voice", )
) )
)
async_add_entities(entities) async_add_entities(entities)
return True return True

View File

@ -10,7 +10,144 @@
}, },
"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."
} }
} }
} }
}
} }

View File

@ -1,15 +1,152 @@
{ {
"config":{ "config": {
"step":{ "step": {
"user":{ "user": {
"data": { "data": {
"api_key": "SmartThings API Token", "api_key": "SmartThings API Token",
"device_id": "Device ID", "device_id": "Device ID",
"device_name":"Device Name", "device_name": "Device Name",
"device_volume": "Max Volume (int)" "device_volume": "Max Volume (int)"
}, },
"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."
}
} }
} }
} }

View File

@ -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"
} }

20
scripts/develop Executable file
View File

@ -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

7
scripts/setup Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/.."
pip install rich pysmartthings