Compare commits
	
		
			1 Commits
		
	
	
		
			main
			...
			fix/ci-job
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 73cb0a2ffe | 
|  | @ -1,32 +0,0 @@ | ||||||
| // 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" |  | ||||||
| } |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| ko_fi: samuelspagl |  | ||||||
|  | @ -15,13 +15,7 @@ jobs: | ||||||
|       contents: write |       contents: write | ||||||
|     steps: |     steps: | ||||||
|       - name: "Checkout the repository" |       - name: "Checkout the repository" | ||||||
|         uses: "actions/checkout@v4.1.0" |         uses: "actions/checkout@v3.5.3" | ||||||
| 
 |  | ||||||
|       - name: "Adjust version number" |  | ||||||
|         shell: "bash" |  | ||||||
|         run: | |  | ||||||
|           yq -i -o json '.version="${{ github.event.release.tag_name }}"' \ |  | ||||||
|             "${{ github.workspace }}/custom_components/samsung_soundbar/manifest.json" |  | ||||||
| 
 | 
 | ||||||
|       - name: "ZIP the integration directory" |       - name: "ZIP the integration directory" | ||||||
|         shell: "bash" |         shell: "bash" | ||||||
|  |  | ||||||
|  | @ -2,12 +2,6 @@ | ||||||
| __pycache__/ | __pycache__/ | ||||||
| *.py[cod] | *.py[cod] | ||||||
| *$py.class | *$py.class | ||||||
| .DS_store |  | ||||||
| 
 |  | ||||||
| config |  | ||||||
| .vscode |  | ||||||
| .ruff_cache |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| # C extensions | # C extensions | ||||||
| *.so | *.so | ||||||
|  |  | ||||||
							
								
								
									
										93
									
								
								CHANGELOG.md
								
								
								
								
							
							
						
						
									
										93
									
								
								CHANGELOG.md
								
								
								
								
							|  | @ -1,93 +0,0 @@ | ||||||
| # 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 |  | ||||||
| 
 |  | ||||||
| ### Added |  | ||||||
| 
 |  | ||||||
| - Icons for the individual entities |  | ||||||
| 
 |  | ||||||
| ### Changed |  | ||||||
| 
 |  | ||||||
| - Updated the GitHub actions workflows |  | ||||||
| - Change "magic numbers" to `MediaPlayerEntityFeature` object |  | ||||||
|   For more information see https://developers.home-assistant.io/blog/2023/12/28/support-feature-magic-numbers-deprecation |  | ||||||
| - the `source` now returns the value `wifi` when the `media_app_name` is `AirPlay` or `Spotify` |  | ||||||
| - removed some unnecessary logging statements, and changed others to `debug` |  | ||||||
| 
 |  | ||||||
| ## [0.2.1] Chore: Format repository - 2024-02-08 |  | ||||||
| 
 |  | ||||||
| ### Changed |  | ||||||
| 
 |  | ||||||
| - formatted the repository with black and isort |  | ||||||
| 
 |  | ||||||
| ## [0.2.0] Add volume sensor - 2024-02-08 |  | ||||||
| 
 |  | ||||||
| ### Added |  | ||||||
| 
 |  | ||||||
| - add new sensor entity for the volume |  | ||||||
|    |  | ||||||
| ### Fix |  | ||||||
| 
 |  | ||||||
| - remove `extra_state_attributes` from `media_player` instance: |  | ||||||
|   The property caused some unwanted side-effects on some systems. |  | ||||||
| 
 |  | ||||||
| ## [0.1.0] 🎉 First Version |  | ||||||
| 
 |  | ||||||
| ### Added |  | ||||||
| 
 |  | ||||||
| - first version, gonna extend this Changelog sometime :D |  | ||||||
							
								
								
									
										21
									
								
								LICENSE
								
								
								
								
							
							
						
						
									
										21
									
								
								LICENSE
								
								
								
								
							|  | @ -1,21 +0,0 @@ | ||||||
| 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,7 +9,8 @@ rich = "*" | ||||||
| homeassistant = "*" | homeassistant = "*" | ||||||
| 
 | 
 | ||||||
| [dev-packages] | [dev-packages] | ||||||
| ruff = "*" | black = "*" | ||||||
|  | isort = "*" | ||||||
| 
 | 
 | ||||||
| [requires] | [requires] | ||||||
| python_version = "3.12" | python_version = "3.11" | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										93
									
								
								README.md
								
								
								
								
							
							
						
						
									
										93
									
								
								README.md
								
								
								
								
							|  | @ -1,22 +1,6 @@ | ||||||
| # YASSI: Yet Another Samsung Soundbar Integration (for Home Assistant) | # HomeAssistant: Samsung Soundbar Integration | ||||||
| 
 | 
 | ||||||
| Welcome to YASSI, the Home Assistant integration designed to bring comprehensive control over your Samsung Soundbar into your smart home ecosystem. | > Yet another Samsung Soundbar Integration (YASSI) | ||||||
| 
 |  | ||||||
| > [!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 | ||||||
| 
 | 
 | ||||||
|  | @ -34,54 +18,47 @@ are not documented... ;) | ||||||
| 
 | 
 | ||||||
| ## Features | ## Features | ||||||
| 
 | 
 | ||||||
|  | - Set-Up through HomeAssistant-UI | ||||||
|  | - Theoretically it should be possible to have multiple Devices (not tested) | ||||||
| 
 | 
 | ||||||
| - **UI Setup**: You can easily set up your Soundbar through the UI. | - `media_player` Entity | ||||||
| - **Media Player Controls**: Power, volume, mute, source selection, and media controls are all at your fingertips. |   - On / Off | ||||||
| - **Selectable Sound Modes**: Choose from various sound modes and inputs for optimal audio. |   - Volume | ||||||
| - **Subwoofer & Equalizer Adjustment**: Fine-tune your audio experience. |   - Mute | ||||||
| - **Switchable Enhancements**: Toggle features like night mode and voice amplification. |   - Input Source | ||||||
| - **Customizable Bass Level**: Set the bass to your preference. |   - Sound Mode | ||||||
| - **Multiple Devices**: should be theoretically possible but **not** tested |   - Media | ||||||
|  |     - 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 | ||||||
|  |   - *[to come] sound mode* (additional control in the "Device" tab) | ||||||
|  |   - *[to come] equalizer preset* | ||||||
| 
 | 
 | ||||||
| For the full feature list per entity type, please take a look at the [documentation](ha-samsung-soundbar.vercel.app) website. | ## How to install it: | ||||||
| 
 | 
 | ||||||
| ## Installation / Setup | ### HACS:  | ||||||
|  | >  ⚠️ not done yet but planned (hopefully) | ||||||
| 
 | 
 | ||||||
| ### Prerequisites | ### Adding this repository as custom repository | ||||||
| 
 | 
 | ||||||
| Before you begin, ensure you have the following: | Add this repository as custom repository in HACS and install it ;) | ||||||
| 
 | 
 | ||||||
| - A Samsung Soundbar compatible with SmartThings. | ### Manual | ||||||
| - Home Assistant installed and running. |  | ||||||
| - HACS (Home Assistant Community Store) for easy installation. |  | ||||||
| 
 | 
 | ||||||
| ### Installation | You can also copy the `samsung_soundbar` folder in the `custom_components` folder to | ||||||
|  | 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 idea on how to do things. | Like already mentioned, thanks to @PiotrMachowski / @thierryBourbon for the general | ||||||
|  | idea on how to do things. | ||||||
|  | @ -11,30 +11,23 @@ from .const import ( | ||||||
|     CONF_ENTRY_DEVICE_ID, |     CONF_ENTRY_DEVICE_ID, | ||||||
|     CONF_ENTRY_DEVICE_NAME, |     CONF_ENTRY_DEVICE_NAME, | ||||||
|     CONF_ENTRY_MAX_VOLUME, |     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, |     SUPPORTED_DOMAINS, | ||||||
|  |     DOMAIN, | ||||||
| ) | ) | ||||||
| from .models import DeviceConfig, SoundbarConfig | from .models import DeviceConfig, SoundbarConfig | ||||||
| 
 | 
 | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| PLATFORMS = ["media_player", "switch", "image", "number", "select", "sensor"] | PLATFORMS = ["media_player", "switch", "image", "number"] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||||||
|     """Set up component from a config entry, config_entry contains data from config entry database.""" |     """Set up component from a config entry, config_entry contains data from config entry database.""" | ||||||
|     # store shell object |     # store shell object | ||||||
| 
 | 
 | ||||||
|     _LOGGER.info(f"[{DOMAIN}] Starting to setup a ConfigEntry") |     _LOGGER.info(f"[{DOMAIN}] Starting to setup ConfigEntry {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.info(f"[{DOMAIN}] Domain not found in hass.data setting default") | ||||||
|         hass.data[DOMAIN] = SoundbarConfig( |         hass.data[DOMAIN] = SoundbarConfig( | ||||||
|             SmartThings( |             SmartThings( | ||||||
|                 async_get_clientsession(hass), entry.data.get(CONF_ENTRY_API_KEY) |                 async_get_clientsession(hass), entry.data.get(CONF_ENTRY_API_KEY) | ||||||
|  | @ -43,11 +36,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     domain_config: SoundbarConfig = hass.data[DOMAIN] |     domain_config: SoundbarConfig = hass.data[DOMAIN] | ||||||
|     _LOGGER.debug(f"[{DOMAIN}] Retrieved Domain Config: {domain_config}") |     _LOGGER.info(f"[{DOMAIN}] Retrieved Domain Config: {domain_config}") | ||||||
| 
 | 
 | ||||||
|     if not entry.data.get(CONF_ENTRY_DEVICE_ID) in domain_config.devices: |     if not entry.data.get(CONF_ENTRY_DEVICE_ID) in domain_config.devices: | ||||||
|         _LOGGER.info(f"[{DOMAIN}] Setting up new Soundbar device") |         _LOGGER.info( | ||||||
|         _LOGGER.debug( |  | ||||||
|             f"[{DOMAIN}] DeviceId: {entry.data.get(CONF_ENTRY_DEVICE_ID)} not found in domain_config, setting up new device." |             f"[{DOMAIN}] DeviceId: {entry.data.get(CONF_ENTRY_DEVICE_ID)} not found in domain_config, setting up new device." | ||||||
|         ) |         ) | ||||||
|         smart_things_device = await domain_config.api.device( |         smart_things_device = await domain_config.api.device( | ||||||
|  | @ -59,18 +51,13 @@ 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( | ||||||
|             entry.data, soundbar_device |             entry.data, | ||||||
|  |             soundbar_device | ||||||
|         ) |         ) | ||||||
|         _LOGGER.info(f"[{DOMAIN}] Successfully initialized new Soundbar device") |         _LOGGER.info(f"[{DOMAIN}] after initializing Soundbar device") | ||||||
| 
 | 
 | ||||||
|     await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) |     await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) | ||||||
|     return True |     return True | ||||||
|  |  | ||||||
|  | @ -1,28 +1,13 @@ | ||||||
| import asyncio |  | ||||||
| import datetime |  | ||||||
| import json | import json | ||||||
| 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 |  | ||||||
| 
 |  | ||||||
| log = logging.getLogger(__name__) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| class SoundbarDevice: | class SoundbarDevice: | ||||||
|     def __init__( |     def __init__( | ||||||
|             self, |         self, device: DeviceEntity, session, max_volume: int, device_name: str | ||||||
|             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 | ||||||
|  | @ -30,21 +15,17 @@ 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 | ||||||
|  | @ -52,7 +33,6 @@ class SoundbarDevice: | ||||||
|         self.__media_title = "" |         self.__media_title = "" | ||||||
|         self.__media_artist = "" |         self.__media_artist = "" | ||||||
|         self.__media_cover_url = "" |         self.__media_cover_url = "" | ||||||
|         self.__media_cover_url_update_time: datetime.datetime | None = None |  | ||||||
|         self.__old_media_title = "" |         self.__old_media_title = "" | ||||||
| 
 | 
 | ||||||
|         self.__max_volume = max_volume |         self.__max_volume = max_volume | ||||||
|  | @ -61,18 +41,12 @@ 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" | ||||||
|         ] |         ] | ||||||
|  | @ -81,29 +55,13 @@ class SoundbarDevice: | ||||||
|         ] |         ] | ||||||
|         if self.__media_title != self.__old_media_title: |         if self.__media_title != self.__old_media_title: | ||||||
|             self.__old_media_title = self.__media_title |             self.__old_media_title = self.__media_title | ||||||
|                 self.__media_cover_url_update_time = datetime.datetime.now() |  | ||||||
|             self.__media_cover_url = await self.get_song_title_artwork( |             self.__media_cover_url = await self.get_song_title_artwork( | ||||||
|                 self.__media_artist, self.__media_title |                 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(1) |  | ||||||
|         payload = await self.get_execute_status() |         payload = await self.get_execute_status() | ||||||
|         retry = 0 |  | ||||||
|         while ( |  | ||||||
|                 "x.com.samsung.networkaudio.supportedSoundmode" not in payload |  | ||||||
|                 and retry < 10 |  | ||||||
|         ): |  | ||||||
|             await asyncio.sleep(1) |  | ||||||
|             payload = await self.get_execute_status() |  | ||||||
|             retry += 1 |  | ||||||
|         if retry == 10: |  | ||||||
|             log.error( |  | ||||||
|                 f"[{DOMAIN}] Error: _update_soundmode exceeded a retry counter of 10" |  | ||||||
|             ) |  | ||||||
|             return |  | ||||||
| 
 |  | ||||||
|         self.__supported_soundmodes = payload[ |         self.__supported_soundmodes = payload[ | ||||||
|             "x.com.samsung.networkaudio.supportedSoundmode" |             "x.com.samsung.networkaudio.supportedSoundmode" | ||||||
|         ] |         ] | ||||||
|  | @ -111,35 +69,13 @@ class SoundbarDevice: | ||||||
| 
 | 
 | ||||||
|     async def _update_woofer(self): |     async def _update_woofer(self): | ||||||
|         await self.update_execution_data(["/sec/networkaudio/woofer"]) |         await self.update_execution_data(["/sec/networkaudio/woofer"]) | ||||||
|         await asyncio.sleep(0.1) |  | ||||||
|         payload = await self.get_execute_status() |         payload = await self.get_execute_status() | ||||||
|         retry = 0 |  | ||||||
|         while "x.com.samsung.networkaudio.woofer" not in payload and retry < 10: |  | ||||||
|             await asyncio.sleep(0.2) |  | ||||||
|             payload = await self.get_execute_status() |  | ||||||
|             retry += 1 |  | ||||||
|         if retry == 10: |  | ||||||
|             log.error( |  | ||||||
|                 f"[{DOMAIN}] Error: _update_woofer exceeded a retry counter of 10" |  | ||||||
|             ) |  | ||||||
|             return |  | ||||||
|         self.__woofer_level = payload["x.com.samsung.networkaudio.woofer"] |         self.__woofer_level = payload["x.com.samsung.networkaudio.woofer"] | ||||||
|         self.__woofer_connection = payload["x.com.samsung.networkaudio.connection"] |         self.__woofer_connection = payload["x.com.samsung.networkaudio.connection"] | ||||||
| 
 | 
 | ||||||
|     async def _update_equalizer(self): |     async def _update_equalizer(self): | ||||||
|         await self.update_execution_data(["/sec/networkaudio/eq"]) |         await self.update_execution_data(["/sec/networkaudio/eq"]) | ||||||
|         await asyncio.sleep(0.1) |  | ||||||
|         payload = await self.get_execute_status() |         payload = await self.get_execute_status() | ||||||
|         retry = 0 |  | ||||||
|         while "x.com.samsung.networkaudio.EQname" not in payload and retry < 10: |  | ||||||
|             await asyncio.sleep(0.2) |  | ||||||
|             payload = await self.get_execute_status() |  | ||||||
|             retry += 1 |  | ||||||
|         if retry == 10: |  | ||||||
|             log.error( |  | ||||||
|                 f"[{DOMAIN}] Error: _update_equalizer exceeded a retry counter of 10" |  | ||||||
|             ) |  | ||||||
|             return |  | ||||||
|         self.__active_eq_preset = payload["x.com.samsung.networkaudio.EQname"] |         self.__active_eq_preset = payload["x.com.samsung.networkaudio.EQname"] | ||||||
|         self.__supported_eq_presets = payload[ |         self.__supported_eq_presets = payload[ | ||||||
|             "x.com.samsung.networkaudio.supportedList" |             "x.com.samsung.networkaudio.supportedList" | ||||||
|  | @ -149,20 +85,7 @@ class SoundbarDevice: | ||||||
| 
 | 
 | ||||||
|     async def _update_advanced_audio(self): |     async def _update_advanced_audio(self): | ||||||
|         await self.update_execution_data(["/sec/networkaudio/advancedaudio"]) |         await self.update_execution_data(["/sec/networkaudio/advancedaudio"]) | ||||||
|         await asyncio.sleep(0.1) |  | ||||||
| 
 |  | ||||||
|         payload = await self.get_execute_status() |         payload = await self.get_execute_status() | ||||||
|         retry = 0 |  | ||||||
|         while "x.com.samsung.networkaudio.nightmode" not in payload and retry < 10: |  | ||||||
|             await asyncio.sleep(0.2) |  | ||||||
|             payload = await self.get_execute_status() |  | ||||||
|             retry += 1 |  | ||||||
|         if retry == 10: |  | ||||||
|             log.error( |  | ||||||
|                 f"[{DOMAIN}] Error: _update_advanced_audio exceeded a retry counter of 10" |  | ||||||
|             ) |  | ||||||
|             return |  | ||||||
| 
 |  | ||||||
|         self.__night_mode = payload["x.com.samsung.networkaudio.nightmode"] |         self.__night_mode = payload["x.com.samsung.networkaudio.nightmode"] | ||||||
|         self.__bass_mode = payload["x.com.samsung.networkaudio.bassboost"] |         self.__bass_mode = payload["x.com.samsung.networkaudio.bassboost"] | ||||||
|         self.__voice_amplifier = payload["x.com.samsung.networkaudio.voiceamplifier"] |         self.__voice_amplifier = payload["x.com.samsung.networkaudio.voiceamplifier"] | ||||||
|  | @ -197,15 +120,7 @@ class SoundbarDevice: | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def state(self) -> str: |     def state(self) -> str: | ||||||
|         if self.device.status.switch: |         return "on" if self.device.status.switch else "off" | ||||||
|             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) | ||||||
|  | @ -217,10 +132,7 @@ class SoundbarDevice: | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def volume_level(self) -> float: |     def volume_level(self) -> float: | ||||||
|         vol = self.device.status.volume |         return ((self.device.status.volume / 100) * self.__max_volume) / 100 | ||||||
|         if vol > self.__max_volume: |  | ||||||
|             return 1.0 |  | ||||||
|         return self.device.status.volume / self.__max_volume |  | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def volume_muted(self) -> bool: |     def volume_muted(self) -> bool: | ||||||
|  | @ -232,7 +144,7 @@ class SoundbarDevice: | ||||||
|         This respects the max volume and hovers between |         This respects the max volume and hovers between | ||||||
|         :param volume: between 0 and 1 |         :param volume: between 0 and 1 | ||||||
|         """ |         """ | ||||||
|         await self.device.set_volume(int(volume * self.__max_volume), True) |         await self.device.set_volume(int(volume * self.__max_volume)) | ||||||
| 
 | 
 | ||||||
|     async def mute_volume(self, mute: bool): |     async def mute_volume(self, mute: bool): | ||||||
|         if mute: |         if mute: | ||||||
|  | @ -262,14 +174,11 @@ class SoundbarDevice: | ||||||
|             property="x.com.samsung.networkaudio.woofer", |             property="x.com.samsung.networkaudio.woofer", | ||||||
|             value=level, |             value=level, | ||||||
|         ) |         ) | ||||||
|         self.__woofer_level = level |  | ||||||
| 
 | 
 | ||||||
|     # ------------ INPUT SOURCE ------------- |     # ------------ INPUT SOURCE ------------- | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def input_source(self): |     def input_source(self): | ||||||
|         if self.media_app_name in ("AirPlay", "Spotify"): |  | ||||||
|             return "wifi" |  | ||||||
|         return self.device.status.input_source |         return self.device.status.input_source | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|  | @ -373,15 +282,11 @@ class SoundbarDevice: | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def media_duration(self) -> int | None: |     def media_duration(self) -> int | None: | ||||||
|         attr = self.device.status.attributes.get("totalTime", None) |         return self.device.status.attributes.get("totalTime").value | ||||||
|         if attr: |  | ||||||
|             return attr.value |  | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def media_position(self) -> int | None: |     def media_position(self) -> int | None: | ||||||
|         attr = self.device.status.attributes.get("elapsedTime", None) |         return self.device.status.attributes.get("elapsedTime").value | ||||||
|         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) | ||||||
|  | @ -392,12 +297,6 @@ 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) | ||||||
|  | @ -405,58 +304,21 @@ class SoundbarDevice: | ||||||
|             return detail_status.value |             return detail_status.value | ||||||
|         return None |         return None | ||||||
| 
 | 
 | ||||||
|     @property |  | ||||||
|     def media_coverart_updated(self) -> datetime.datetime: |  | ||||||
|         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): | ||||||
|         stuff = await self.device.command("main", "execute", "execute", argument) |         return 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}] | ||||||
|         assert await self.device.command("main", "execute", "execute", argument) |         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_stuff = await resp.json() |         dict = await resp.json() | ||||||
|         return dict_stuff["data"]["value"]["payload"] |         return dict["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: | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|  | @ -1,15 +0,0 @@ | ||||||
| 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,22 +1,16 @@ | ||||||
| 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 ( | from .const import ( | ||||||
|     CONF_ENTRY_API_KEY, |     CONF_ENTRY_API_KEY, | ||||||
|     CONF_ENTRY_DEVICE_ID, |     CONF_ENTRY_DEVICE_ID, | ||||||
|     CONF_ENTRY_DEVICE_NAME, |     CONF_ENTRY_DEVICE_NAME, | ||||||
|     CONF_ENTRY_MAX_VOLUME, |     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, |     DOMAIN, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -33,9 +27,24 @@ 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): | ||||||
|  |         _LOGGER.error(f"Example Flow starts with user_input {user_input}") | ||||||
|         if user_input is not None: |         if user_input is not None: | ||||||
|             self.user_input = user_input |             _LOGGER.error(f"User Input is not filled") | ||||||
|             return await self.async_step_device() |             try: | ||||||
|  |                 session = async_get_clientsession(self.hass) | ||||||
|  |                 api = pysmartthings.SmartThings( | ||||||
|  |                     session, user_input.get(CONF_ENTRY_API_KEY) | ||||||
|  |                 ) | ||||||
|  |                 _LOGGER.error(f"Validating Input {user_input}") | ||||||
|  |                 device = await validate_input(api, user_input.get(CONF_ENTRY_DEVICE_ID)) | ||||||
|  | 
 | ||||||
|  |                 _LOGGER.error( | ||||||
|  |                     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"Example Flow 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", | ||||||
|  | @ -44,98 +53,7 @@ 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( |                     vol.Required(CONF_ENTRY_MAX_VOLUME): int, | ||||||
|                         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,12 +9,6 @@ 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,14 +1,12 @@ | ||||||
| import logging | import logging | ||||||
| from datetime import datetime |  | ||||||
| 
 | 
 | ||||||
| from homeassistant.components.image import ImageEntity | from homeassistant.components.image import ImageEntity | ||||||
| from homeassistant.core import HomeAssistant | from homeassistant.core import HomeAssistant | ||||||
| from homeassistant.helpers.entity import DeviceInfo | from homeassistant.helpers.entity import DeviceInfo | ||||||
| from homeassistant.helpers.typing import UndefinedType |  | ||||||
| 
 | 
 | ||||||
| from .api_extension.SoundbarDevice import SoundbarDevice |  | ||||||
| from .const import CONF_ENTRY_DEVICE_ID, DOMAIN |  | ||||||
| from .models import DeviceConfig | from .models import DeviceConfig | ||||||
|  | from .api_extension.SoundbarDevice import SoundbarDevice | ||||||
|  | from .const import DOMAIN, CONF_ENTRY_DEVICE_ID | ||||||
| 
 | 
 | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | @ -43,22 +41,9 @@ class SoundbarImageEntity(ImageEntity): | ||||||
|             sw_version=self.__device.firmware_version, |             sw_version=self.__device.firmware_version, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         self.__updated = None |         self._attr_image_url = self.__device.media_coverart_url | ||||||
| 
 | 
 | ||||||
|     # ---------- GENERAL --------------- |     # ---------- GENERAL --------------- | ||||||
|     @property |  | ||||||
|     def image_url(self) -> str | None | UndefinedType: |  | ||||||
|         """Return URL of image.""" |  | ||||||
|         return self.__device.media_coverart_url |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def image_last_updated(self) -> datetime | None: |  | ||||||
|         """The time when the image was last updated.""" |  | ||||||
|         current = self.__device.media_coverart_updated |  | ||||||
|         if self.__updated != current: |  | ||||||
|             self._cached_image = None |  | ||||||
|             self.__updated = current |  | ||||||
|         return current |  | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def name(self): |     def name(self): | ||||||
|  |  | ||||||
|  | @ -1,16 +1,12 @@ | ||||||
| { | { | ||||||
|   "domain": "samsung_soundbar", |   "domain": "samsung_soundbar", | ||||||
|   "name": "Samsung Soundbar", |   "name": "Samsung Soundbar", | ||||||
|   "codeowners": [ |   "codeowners": ["@samuelspagl"], | ||||||
|     "@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": [ |   "requirements": ["pysmartthings"], | ||||||
|     "pysmartthings==0.7.8" |   "version": "0.1.0" | ||||||
|   ], |  | ||||||
|   "version": "0.4.1" |  | ||||||
| } | } | ||||||
|  | @ -1,18 +1,26 @@ | ||||||
| import logging | import logging | ||||||
| from typing import Any, Mapping |  | ||||||
| 
 | 
 | ||||||
| from homeassistant.components.media_player import ( | from homeassistant.components.media_player import ( | ||||||
|     DEVICE_CLASS_SPEAKER, |     DEVICE_CLASS_SPEAKER, | ||||||
|     MediaPlayerEntity, |     MediaPlayerEntity, | ||||||
| ) | ) | ||||||
| from homeassistant.components.media_player.const import MediaPlayerEntityFeature | from homeassistant.components.media_player.const import ( | ||||||
|  |     SUPPORT_PAUSE, | ||||||
|  |     SUPPORT_PLAY, | ||||||
|  |     SUPPORT_SELECT_SOUND_MODE, | ||||||
|  |     SUPPORT_SELECT_SOURCE, | ||||||
|  |     SUPPORT_STOP, | ||||||
|  |     SUPPORT_TURN_OFF, | ||||||
|  |     SUPPORT_TURN_ON, | ||||||
|  |     SUPPORT_VOLUME_MUTE, | ||||||
|  |     SUPPORT_VOLUME_SET, | ||||||
|  |     SUPPORT_VOLUME_STEP, | ||||||
|  | ) | ||||||
| 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 .models import DeviceConfig | ||||||
| from .api_extension.SoundbarDevice import SoundbarDevice | from .api_extension.SoundbarDevice import SoundbarDevice | ||||||
| from .api_extension.const import SpeakerIdentifier, RearSpeakerMode |  | ||||||
| from .const import ( | from .const import ( | ||||||
|     CONF_ENTRY_API_KEY, |     CONF_ENTRY_API_KEY, | ||||||
|     CONF_ENTRY_DEVICE_ID, |     CONF_ENTRY_DEVICE_ID, | ||||||
|  | @ -20,7 +28,6 @@ from .const import ( | ||||||
|     CONF_ENTRY_MAX_VOLUME, |     CONF_ENTRY_MAX_VOLUME, | ||||||
|     DOMAIN, |     DOMAIN, | ||||||
| ) | ) | ||||||
| from .models import DeviceConfig |  | ||||||
| 
 | 
 | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | @ -28,90 +35,22 @@ DEFAULT_NAME = "SmartThings Soundbar" | ||||||
| CONF_MAX_VOLUME = "max_volume" | CONF_MAX_VOLUME = "max_volume" | ||||||
| 
 | 
 | ||||||
| SUPPORT_SMARTTHINGS_SOUNDBAR = ( | SUPPORT_SMARTTHINGS_SOUNDBAR = ( | ||||||
|     MediaPlayerEntityFeature.PAUSE |     SUPPORT_PAUSE | ||||||
|     | MediaPlayerEntityFeature.VOLUME_STEP |     | SUPPORT_VOLUME_STEP | ||||||
|     | MediaPlayerEntityFeature.VOLUME_MUTE |     | SUPPORT_VOLUME_MUTE | ||||||
|     | MediaPlayerEntityFeature.VOLUME_SET |     | SUPPORT_VOLUME_SET | ||||||
|     | MediaPlayerEntityFeature.SELECT_SOURCE |     | SUPPORT_SELECT_SOURCE | ||||||
|     | MediaPlayerEntityFeature.TURN_OFF |     | SUPPORT_TURN_OFF | ||||||
|     | MediaPlayerEntityFeature.TURN_ON |     | SUPPORT_TURN_ON | ||||||
|     | MediaPlayerEntityFeature.PLAY |     | SUPPORT_PLAY | ||||||
|     | MediaPlayerEntityFeature.NEXT_TRACK |     | SUPPORT_STOP | ||||||
|     | MediaPlayerEntityFeature.PREVIOUS_TRACK |     | SUPPORT_SELECT_SOUND_MODE | ||||||
|     | MediaPlayerEntityFeature.STOP |  | ||||||
|     | 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] | ||||||
|  | @ -248,47 +187,5 @@ 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 |  | ||||||
|     # Still enabling this can cause side-effects. |  | ||||||
|     # @property |  | ||||||
|     # def extra_state_attributes(self) -> Mapping[str, Any] | None: |  | ||||||
|     #     return {"device_information": self.device.retrieve_data} |  | ||||||
|  |  | ||||||
|  | @ -1,15 +1,11 @@ | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from homeassistant.components.number import ( | from homeassistant.components.number import NumberEntity | ||||||
|     NumberEntity, |  | ||||||
|     NumberEntityDescription, |  | ||||||
|     NumberMode, |  | ||||||
| ) |  | ||||||
| from homeassistant.helpers.entity import DeviceInfo | from homeassistant.helpers.entity import DeviceInfo | ||||||
| 
 | 
 | ||||||
| from .api_extension.SoundbarDevice import SoundbarDevice |  | ||||||
| from .const import CONF_ENTRY_DEVICE_ID, CONF_ENTRY_SETTINGS_WOOFER_NUMBER, DOMAIN |  | ||||||
| from .models import DeviceConfig | from .models import DeviceConfig | ||||||
|  | from .api_extension.SoundbarDevice import SoundbarDevice | ||||||
|  | from .const import CONF_ENTRY_DEVICE_ID, DOMAIN | ||||||
| 
 | 
 | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | @ -21,34 +17,31 @@ 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( |         if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID): | ||||||
|             CONF_ENTRY_DEVICE_ID |  | ||||||
|         ) and config_entry.data.get(CONF_ENTRY_SETTINGS_WOOFER_NUMBER): |  | ||||||
|             entities.append( |             entities.append( | ||||||
|                 SoundbarWooferNumberEntity( |                 SoundbarNumberEntity( | ||||||
|                     device, |                     device, | ||||||
|                     "woofer_level", |                     "woofer_level", | ||||||
|  |                     device.woofer_level, | ||||||
|  |                     device.set_woofer, | ||||||
|  |                     (-6, 12), | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|     async_add_entities(entities) |     async_add_entities(entities) | ||||||
|     return True |     return True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class SoundbarWooferNumberEntity(NumberEntity): | class SoundbarNumberEntity(NumberEntity): | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, |         self, | ||||||
|         device: SoundbarDevice, |         device: SoundbarDevice, | ||||||
|         append_unique_id: str, |         append_unique_id: str, | ||||||
|  |         state_function, | ||||||
|  |         on_function, | ||||||
|  |         min_max: tuple, | ||||||
|     ): |     ): | ||||||
|         self.entity_id = f"number.{device.device_name}_{append_unique_id}" |         self.entity_id = f"number.{device.device_name}_{append_unique_id}" | ||||||
|         self.entity_description = NumberEntityDescription( | 
 | ||||||
|             native_max_value=6, |  | ||||||
|             native_min_value=-10, |  | ||||||
|             mode=NumberMode.BOX, |  | ||||||
|             native_step=1, |  | ||||||
|             native_unit_of_measurement="dB", |  | ||||||
|             key=append_unique_id, |  | ||||||
|         ) |  | ||||||
|         self.__device = device |         self.__device = device | ||||||
|         self._attr_unique_id = f"{device.device_id}_sw_{append_unique_id}" |         self._attr_unique_id = f"{device.device_id}_sw_{append_unique_id}" | ||||||
|         self._attr_device_info = DeviceInfo( |         self._attr_device_info = DeviceInfo( | ||||||
|  | @ -58,19 +51,27 @@ class SoundbarWooferNumberEntity(NumberEntity): | ||||||
|             model=self.__device.model, |             model=self.__device.model, | ||||||
|             sw_version=self.__device.firmware_version, |             sw_version=self.__device.firmware_version, | ||||||
|         ) |         ) | ||||||
|         self.__append_unique_id = append_unique_id | 
 | ||||||
|  |         self.__current_value_function = state_function | ||||||
|  |         self.__set_value_function = on_function | ||||||
|  |         self.__min_value = min_max[0] | ||||||
|  |         self.__max_value = min_max[1] | ||||||
| 
 | 
 | ||||||
|     # ---------- GENERAL --------------- |     # ---------- GENERAL --------------- | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def name(self): |     def name(self): | ||||||
|         return self.__append_unique_id |         return self.__device.device_name | ||||||
| 
 | 
 | ||||||
|     # ------ STATE FUNCTIONS -------- |     # ------ STATE FUNCTIONS -------- | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def native_value(self) -> float | None: |     def native_value(self) -> float | None: | ||||||
|         return self.__device.woofer_level |         return self.__current_value_function | ||||||
| 
 | 
 | ||||||
|     async def async_set_native_value(self, value: float): |     async def async_set_native_value(self, value: float): | ||||||
|         await self.__device.set_woofer(int(value)) |         if value > self.__max_value: | ||||||
|  |             value = self.__min_value | ||||||
|  |         if value < self.__min_value: | ||||||
|  |             value = self.__min_value | ||||||
|  |         await self.__set_value_function(value) | ||||||
|  |  | ||||||
|  | @ -1,189 +0,0 @@ | ||||||
| import logging |  | ||||||
| 
 |  | ||||||
| from homeassistant.components.number import ( |  | ||||||
|     NumberEntity, |  | ||||||
|     NumberEntityDescription, |  | ||||||
|     NumberMode, |  | ||||||
| ) |  | ||||||
| from homeassistant.components.select import SelectEntity, SelectEntityDescription |  | ||||||
| from homeassistant.helpers.entity import DeviceInfo |  | ||||||
| 
 |  | ||||||
| from .api_extension.SoundbarDevice import SoundbarDevice |  | ||||||
| from .const import ( |  | ||||||
|     CONF_ENTRY_DEVICE_ID, |  | ||||||
|     CONF_ENTRY_SETTINGS_EQ_SELECTOR, |  | ||||||
|     CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR, |  | ||||||
|     DOMAIN, |  | ||||||
| ) |  | ||||||
| from .models import DeviceConfig |  | ||||||
| 
 |  | ||||||
| _LOGGER = logging.getLogger(__name__) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| async def async_setup_entry(hass, config_entry, async_add_entities): |  | ||||||
|     domain_data = hass.data[DOMAIN] |  | ||||||
|     entities = [] |  | ||||||
|     for key in domain_data.devices: |  | ||||||
|         device_config: DeviceConfig = domain_data.devices[key] |  | ||||||
|         device = device_config.device |  | ||||||
|         if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID): |  | ||||||
|             if config_entry.data.get(CONF_ENTRY_SETTINGS_EQ_SELECTOR): |  | ||||||
|                 entities.append( |  | ||||||
|                     EqPresetSelectEntity(device, "eq_preset", "mdi:tune-vertical") |  | ||||||
|                 ) |  | ||||||
|             if config_entry.data.get(CONF_ENTRY_SETTINGS_SOUNDMODE_SELECTOR): |  | ||||||
|                 entities.append( |  | ||||||
|                     SoundModeSelectEntity( |  | ||||||
|                         device, "sound_mode_preset", "mdi:surround-sound" |  | ||||||
|                     ) |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|             entities.append( |  | ||||||
|                 InputSelectEntity(device, "input_preset", "mdi:video-input-hdmi") |  | ||||||
|             ) |  | ||||||
|     async_add_entities(entities) |  | ||||||
|     return True |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class EqPresetSelectEntity(SelectEntity): |  | ||||||
|     def __init__( |  | ||||||
|         self, |  | ||||||
|         device: SoundbarDevice, |  | ||||||
|         append_unique_id: str, |  | ||||||
|         icon_string: str, |  | ||||||
|     ): |  | ||||||
|         self.entity_id = f"number.{device.device_name}_{append_unique_id}" |  | ||||||
|         self.entity_description = SelectEntityDescription( |  | ||||||
|             key=append_unique_id, |  | ||||||
|         ) |  | ||||||
|         self.__base_icon = icon_string |  | ||||||
|         self.__device = device |  | ||||||
|         self._attr_unique_id = f"{device.device_id}_sw_{append_unique_id}" |  | ||||||
|         self._attr_device_info = DeviceInfo( |  | ||||||
|             identifiers={(DOMAIN, self.__device.device_id)}, |  | ||||||
|             name=self.__device.device_name, |  | ||||||
|             manufacturer=self.__device.manufacturer, |  | ||||||
|             model=self.__device.model, |  | ||||||
|             sw_version=self.__device.firmware_version, |  | ||||||
|         ) |  | ||||||
|         self.__append_unique_id = append_unique_id |  | ||||||
| 
 |  | ||||||
|         self._attr_options = self.__device.supported_equalizer_presets |  | ||||||
| 
 |  | ||||||
|     # ---------- GENERAL --------------- |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def name(self): |  | ||||||
|         return self.__append_unique_id |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def icon(self) -> str | None: |  | ||||||
|         return self.__base_icon |  | ||||||
| 
 |  | ||||||
|     # ------ STATE FUNCTIONS -------- |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def current_option(self) -> str | None: |  | ||||||
|         """Get the current status of the select entity from device_status.""" |  | ||||||
|         return self.__device.active_equalizer_preset |  | ||||||
| 
 |  | ||||||
|     async def async_select_option(self, option: str) -> None: |  | ||||||
|         """Set the option.""" |  | ||||||
| 
 |  | ||||||
|         await self.__device.set_equalizer_preset(option) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class SoundModeSelectEntity(SelectEntity): |  | ||||||
|     def __init__( |  | ||||||
|         self, |  | ||||||
|         device: SoundbarDevice, |  | ||||||
|         append_unique_id: str, |  | ||||||
|         icon_string: str, |  | ||||||
|     ): |  | ||||||
|         self.entity_id = f"number.{device.device_name}_{append_unique_id}" |  | ||||||
|         self.entity_description = SelectEntityDescription( |  | ||||||
|             key=append_unique_id, |  | ||||||
|         ) |  | ||||||
|         self.__base_icon = icon_string |  | ||||||
|         self.__device = device |  | ||||||
|         self._attr_unique_id = f"{device.device_id}_sw_{append_unique_id}" |  | ||||||
|         self._attr_device_info = DeviceInfo( |  | ||||||
|             identifiers={(DOMAIN, self.__device.device_id)}, |  | ||||||
|             name=self.__device.device_name, |  | ||||||
|             manufacturer=self.__device.manufacturer, |  | ||||||
|             model=self.__device.model, |  | ||||||
|             sw_version=self.__device.firmware_version, |  | ||||||
|         ) |  | ||||||
|         self.__append_unique_id = append_unique_id |  | ||||||
| 
 |  | ||||||
|         self._attr_options = self.__device.supported_soundmodes |  | ||||||
| 
 |  | ||||||
|     # ---------- GENERAL --------------- |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def name(self): |  | ||||||
|         return self.__append_unique_id |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def icon(self) -> str | None: |  | ||||||
|         return self.__base_icon |  | ||||||
| 
 |  | ||||||
|     # ------ STATE FUNCTIONS -------- |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def current_option(self) -> str | None: |  | ||||||
|         """Get the current status of the select entity from device_status.""" |  | ||||||
|         return self.__device.sound_mode |  | ||||||
| 
 |  | ||||||
|     async def async_select_option(self, option: str) -> None: |  | ||||||
|         """Set the option.""" |  | ||||||
| 
 |  | ||||||
|         await self.__device.select_sound_mode(option) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class InputSelectEntity(SelectEntity): |  | ||||||
|     def __init__( |  | ||||||
|         self, |  | ||||||
|         device: SoundbarDevice, |  | ||||||
|         append_unique_id: str, |  | ||||||
|         icon_string: str, |  | ||||||
|     ): |  | ||||||
|         self.entity_id = f"number.{device.device_name}_{append_unique_id}" |  | ||||||
|         self.entity_description = SelectEntityDescription( |  | ||||||
|             key=append_unique_id, |  | ||||||
|         ) |  | ||||||
|         self.__base_icon = icon_string |  | ||||||
|         self.__device = device |  | ||||||
|         self._attr_unique_id = f"{device.device_id}_sw_{append_unique_id}" |  | ||||||
|         self._attr_device_info = DeviceInfo( |  | ||||||
|             identifiers={(DOMAIN, self.__device.device_id)}, |  | ||||||
|             name=self.__device.device_name, |  | ||||||
|             manufacturer=self.__device.manufacturer, |  | ||||||
|             model=self.__device.model, |  | ||||||
|             sw_version=self.__device.firmware_version, |  | ||||||
|         ) |  | ||||||
|         self.__append_unique_id = append_unique_id |  | ||||||
| 
 |  | ||||||
|         self._attr_options = self.__device.supported_input_sources |  | ||||||
| 
 |  | ||||||
|     # ---------- GENERAL --------------- |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def name(self): |  | ||||||
|         return self.__append_unique_id |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def icon(self) -> str | None: |  | ||||||
|         return self.__base_icon |  | ||||||
| 
 |  | ||||||
|     # ------ STATE FUNCTIONS -------- |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def current_option(self) -> str | None: |  | ||||||
|         """Get the current status of the select entity from device_status.""" |  | ||||||
|         return self.__device.input_source |  | ||||||
| 
 |  | ||||||
|     async def async_select_option(self, option: str) -> None: |  | ||||||
|         """Set the option.""" |  | ||||||
| 
 |  | ||||||
|         await self.__device.select_source(option) |  | ||||||
|  | @ -1,56 +0,0 @@ | ||||||
| import logging |  | ||||||
| 
 |  | ||||||
| from homeassistant.components.sensor import ( |  | ||||||
|     SensorDeviceClass, |  | ||||||
|     SensorEntity, |  | ||||||
|     SensorStateClass, |  | ||||||
| ) |  | ||||||
| from homeassistant.helpers.entity import DeviceInfo |  | ||||||
| 
 |  | ||||||
| from .api_extension.SoundbarDevice import SoundbarDevice |  | ||||||
| from .const import CONF_ENTRY_DEVICE_ID, DOMAIN |  | ||||||
| from .models import DeviceConfig |  | ||||||
| 
 |  | ||||||
| _LOGGER = logging.getLogger(__name__) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| async def async_setup_entry(hass, config_entry, async_add_entities): |  | ||||||
|     domain_data = hass.data[DOMAIN] |  | ||||||
|     entities = [] |  | ||||||
|     for key in domain_data.devices: |  | ||||||
|         device_config: DeviceConfig = domain_data.devices[key] |  | ||||||
|         device = device_config.device |  | ||||||
| 
 |  | ||||||
|         if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID): |  | ||||||
|             entities.append(VolumeSensor(device, "volume_level", "mdi:volume-high")) |  | ||||||
|     async_add_entities(entities) |  | ||||||
|     return True |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class VolumeSensor(SensorEntity): |  | ||||||
|     def __init__(self, device: SoundbarDevice, append_unique_id: str, icon_string: str): |  | ||||||
|         self.entity_id = f"sensor.{device.device_name}_{append_unique_id}" |  | ||||||
|         self.__device = device |  | ||||||
|         self._attr_unique_id = f"{device.device_id}_sw_{append_unique_id}" |  | ||||||
|         self.__base_icon = icon_string |  | ||||||
|         self._attr_device_info = DeviceInfo( |  | ||||||
|             identifiers={(DOMAIN, self.__device.device_id)}, |  | ||||||
|             name=self.__device.device_name, |  | ||||||
|             manufacturer=self.__device.manufacturer, |  | ||||||
|             model=self.__device.model, |  | ||||||
|             sw_version=self.__device.firmware_version, |  | ||||||
|         ) |  | ||||||
|         self.__append_unique_id = append_unique_id |  | ||||||
| 
 |  | ||||||
|         _attr_device_class = SensorDeviceClass.VOLUME |  | ||||||
| 
 |  | ||||||
|     @property |  | ||||||
|     def icon(self) -> str | None: |  | ||||||
|         return self.__base_icon |  | ||||||
| 
 |  | ||||||
|     def update(self) -> None: |  | ||||||
|         """Fetch new state data for the sensor. |  | ||||||
| 
 |  | ||||||
|         This is the only method that should fetch new data for Home Assistant. |  | ||||||
|         """ |  | ||||||
|         self._attr_native_value = self.__device.device.status.volume |  | ||||||
|  | @ -1,167 +0,0 @@ | ||||||
| 
 |  | ||||||
| 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: |  | ||||||
|  | @ -3,13 +3,9 @@ import logging | ||||||
| from homeassistant.components.switch import SwitchEntity | 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 .const import ( |  | ||||||
|     CONF_ENTRY_DEVICE_ID, |  | ||||||
|     CONF_ENTRY_SETTINGS_ADVANCED_AUDIO_SWITCHES, |  | ||||||
|     DOMAIN, |  | ||||||
| ) |  | ||||||
| from .models import DeviceConfig | from .models import DeviceConfig | ||||||
|  | from .api_extension.SoundbarDevice import SoundbarDevice | ||||||
|  | from .const import CONF_ENTRY_DEVICE_ID, DOMAIN | ||||||
| 
 | 
 | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | @ -22,7 +18,6 @@ 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, | ||||||
|  | @ -30,7 +25,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): | ||||||
|                     lambda: device.night_mode, |                     lambda: device.night_mode, | ||||||
|                     device.set_night_mode, |                     device.set_night_mode, | ||||||
|                     device.set_night_mode, |                     device.set_night_mode, | ||||||
|                         "mdi:weather-night", |  | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             entities.append( |             entities.append( | ||||||
|  | @ -40,7 +34,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): | ||||||
|                     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", |  | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|             entities.append( |             entities.append( | ||||||
|  | @ -50,7 +43,6 @@ async def async_setup_entry(hass, config_entry, async_add_entities): | ||||||
|                     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", |  | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|     async_add_entities(entities) |     async_add_entities(entities) | ||||||
|  | @ -65,14 +57,12 @@ class SoundbarSwitchAdvancedAudio(SwitchEntity): | ||||||
|         state_function, |         state_function, | ||||||
|         on_function, |         on_function, | ||||||
|         off_function, |         off_function, | ||||||
|         icon_string: str = "mdi:toggle-switch-variant", |  | ||||||
|     ): |     ): | ||||||
|         self.entity_id = f"switch.{device.device_name}_{append_unique_id}" |         self.entity_id = f"switch.{device.device_name}_{append_unique_id}" | ||||||
| 
 | 
 | ||||||
|         self.__device = device |         self.__device = device | ||||||
|         self._name = f"{self.__device.device_name} {append_unique_id}" |         self._name = f"{self.__device.device_name} {append_unique_id}" | ||||||
|         self._attr_unique_id = f"{device.device_id}_sw_{append_unique_id}" |         self._attr_unique_id = f"{device.device_id}_sw_{append_unique_id}" | ||||||
|         self.__base_icon = icon_string |  | ||||||
|         self._attr_device_info = DeviceInfo( |         self._attr_device_info = DeviceInfo( | ||||||
|             identifiers={(DOMAIN, self.__device.device_id)}, |             identifiers={(DOMAIN, self.__device.device_id)}, | ||||||
|             name=self.__device.device_name, |             name=self.__device.device_name, | ||||||
|  | @ -95,10 +85,6 @@ class SoundbarSwitchAdvancedAudio(SwitchEntity): | ||||||
|     def update(self): |     def update(self): | ||||||
|         self.__state = self.__state_function() |         self.__state = self.__state_function() | ||||||
| 
 | 
 | ||||||
|     @property |  | ||||||
|     def icon(self) -> str | None: |  | ||||||
|         return self.__base_icon |  | ||||||
| 
 |  | ||||||
|     # ------ STATE FUNCTIONS -------- |     # ------ STATE FUNCTIONS -------- | ||||||
|     @property |     @property | ||||||
|     def state(self): |     def state(self): | ||||||
|  |  | ||||||
|  | @ -10,143 +10,6 @@ | ||||||
|                   }, |                   }, | ||||||
|                   "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." |  | ||||||
|             } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,152 +1,15 @@ | ||||||
| { | { | ||||||
|     "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." |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| dist |  | ||||||
| node_modules |  | ||||||
| .output |  | ||||||
| .nuxt |  | ||||||
|  | @ -1,8 +0,0 @@ | ||||||
| module.exports = { |  | ||||||
|   root: true, |  | ||||||
|   extends: '@nuxt/eslint-config', |  | ||||||
|   rules: { |  | ||||||
|     'vue/max-attributes-per-line': 'off', |  | ||||||
|     'vue/multi-word-component-names': 'off' |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | @ -1,12 +0,0 @@ | ||||||
| node_modules |  | ||||||
| *.iml |  | ||||||
| .idea |  | ||||||
| *.log* |  | ||||||
| .nuxt |  | ||||||
| .vscode |  | ||||||
| .DS_Store |  | ||||||
| coverage |  | ||||||
| dist |  | ||||||
| sw.* |  | ||||||
| .env |  | ||||||
| .output |  | ||||||
|  | @ -1,2 +0,0 @@ | ||||||
| shamefully-hoist=true |  | ||||||
| strict-peer-dependencies=false |  | ||||||
|  | @ -1,57 +0,0 @@ | ||||||
| # Docus Starter |  | ||||||
| 
 |  | ||||||
| Starter template for [Docus](https://docus.dev). |  | ||||||
| 
 |  | ||||||
| ## Clone |  | ||||||
| 
 |  | ||||||
| Clone the repository (using `nuxi`): |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| npx nuxi init -t themes/docus |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Setup |  | ||||||
| 
 |  | ||||||
| Install dependencies: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| yarn install |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Development |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| yarn dev |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Edge Side Rendering |  | ||||||
| 
 |  | ||||||
| Can be deployed to Vercel Functions, Netlify Functions, AWS, and most Node-compatible environments. |  | ||||||
| 
 |  | ||||||
| Look at all the available presets [here](https://v3.nuxtjs.org/guide/deploy/presets). |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| yarn build |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Static Generation |  | ||||||
| 
 |  | ||||||
| Use the `generate` command to build your application. |  | ||||||
| 
 |  | ||||||
| The HTML files will be generated in the .output/public directory and ready to be deployed to any static compatible hosting. |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| yarn generate |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Preview build |  | ||||||
| 
 |  | ||||||
| You might want to preview the result of your build locally, to do so, run the following command: |  | ||||||
| 
 |  | ||||||
| ```bash |  | ||||||
| yarn preview |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| --- |  | ||||||
| 
 |  | ||||||
| For a detailed explanation of how things work, check out [Docus](https://docus.dev). |  | ||||||
|  | @ -1,37 +0,0 @@ | ||||||
| export default defineAppConfig({ |  | ||||||
|   docus: { |  | ||||||
|     title: '🔊 Yassi', |  | ||||||
|     description: 'Yet another Samsung Soundbar integration for Home Assistant', |  | ||||||
|     image: 'https://user-images.githubusercontent.com/904724/185365452-87b7ca7b-6030-4813-a2db-5e65c785bf88.png', |  | ||||||
|     socials: { |  | ||||||
|       github: 'samuelspagl/ha_samsung_soundbar', |  | ||||||
|       nuxt: { |  | ||||||
|         label: 'Nuxt', |  | ||||||
|         icon: 'simple-icons:nuxtdotjs', |  | ||||||
|         href: 'https://nuxt.com' |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     github: { |  | ||||||
|       dir: 'docs/content', |  | ||||||
|       branch: 'main', |  | ||||||
|       repo: 'ha_samsung_soundbar', |  | ||||||
|       owner: 'samuelspagl', |  | ||||||
|       edit: true |  | ||||||
|     }, |  | ||||||
|     aside: { |  | ||||||
|       level: 0, |  | ||||||
|       collapsed: false, |  | ||||||
|       exclude: [] |  | ||||||
|     }, |  | ||||||
|     main: { |  | ||||||
|       padded: true, |  | ||||||
|       fluid: true |  | ||||||
|     }, |  | ||||||
|     header: { |  | ||||||
|       logo: false, |  | ||||||
|       showLinkIcon: true, |  | ||||||
|       exclude: [], |  | ||||||
|       fluid: true |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| }) |  | ||||||
|  | @ -1,70 +0,0 @@ | ||||||
| --- |  | ||||||
| title: "YASSI" |  | ||||||
| --- |  | ||||||
| ::block-hero |  | ||||||
| --- |  | ||||||
| cta: |  | ||||||
|   - Why another HomeAssistant integration? |  | ||||||
|   - /first-things-first/why-another-integration |  | ||||||
| secondary: |  | ||||||
|   - Open on GitHub → |  | ||||||
|   - https://github.com/samuelspagl/ha_samsung_soundbar |  | ||||||
| --- |  | ||||||
| 
 |  | ||||||
| #title |  | ||||||
| Yassi - Yet another Samsung Soundbar integration |  | ||||||
| 
 |  | ||||||
| #description |  | ||||||
| **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 |  | ||||||
| #title |  | ||||||
| Quick-Start |  | ||||||
| #root |  | ||||||
| :ellipsis |  | ||||||
| 
 |  | ||||||
| #default |  | ||||||
|   ::card |  | ||||||
|   #title |  | ||||||
|   ❓ Why another integration? |  | ||||||
|   #description |  | ||||||
|   Whether you thought about it or not, here is the answer ;). |  | ||||||
|   <br> |  | ||||||
|   <br> |  | ||||||
|   :button-link[click here]{href="/first-things-first/why-another-integration"} |  | ||||||
|   :: |  | ||||||
| 
 |  | ||||||
|   ::card |  | ||||||
|   #title |  | ||||||
|   🚀 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 |  | ||||||
|   Many cool features are awaiting your eyes to see ✨. |  | ||||||
|   <br> |  | ||||||
|   <br> |  | ||||||
|   :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"} |  | ||||||
|   :: |  | ||||||
| :: |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| # ❓ 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... ;)  |  | ||||||
|  | @ -1,43 +0,0 @@ | ||||||
| # 🚀 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. |  | ||||||
| :: |  | ||||||
|  | @ -1,17 +0,0 @@ | ||||||
| # ‼️ 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,52 +0,0 @@ | ||||||
| # ✨ Features Overview |  | ||||||
| 
 |  | ||||||
| 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` Entity |  | ||||||
| 
 |  | ||||||
| | **Feature**       | **Capability** | **Access Type** | |  | ||||||
| |-------------------|----------------|-----------------| |  | ||||||
| | Power             | on / off       | 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    | |  | ||||||
| 
 |  | ||||||
| ## `number` Entity |  | ||||||
| 
 |  | ||||||
| | **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. |  | ||||||
|  | @ -1,47 +0,0 @@ | ||||||
| # "Standard" information |  | ||||||
| 
 |  | ||||||
| This is the "standard" information that you can fetch with the `pysmartthings` library |  | ||||||
| for a given soundbar: |  | ||||||
| 
 |  | ||||||
| ```python |  | ||||||
| { |  | ||||||
|     'supportedPlaybackCommands': status(value=['play', 'pause', 'stop'], unit=None, data=None), |  | ||||||
|     'playbackStatus': status(value='paused', unit=None, data=None), |  | ||||||
|     'mode': status(value=10, unit=None, data=None), |  | ||||||
|     'detailName': status(value='TV', unit=None, data=None), |  | ||||||
|     'volume': status(value=16, unit='%', data=None), |  | ||||||
|     'supportedInputSources': status(value=['digital', 'HDMI1', 'bluetooth', 'wifi'], unit=None, data=None), |  | ||||||
|     'inputSource': status(value='digital', unit=None, data=None), |  | ||||||
|     'data': status(value=None,unit=None,data=None), |  | ||||||
|     'switch': status(value='on', unit=None, data=None), |  | ||||||
|     'role': status(value=None, unit=None, data=None), |  | ||||||
|     'channel': status(value=None, unit=None, data=None), |  | ||||||
|     'masterName': status(value=None, unit=None, data=None), |  | ||||||
|     'status': status(value=None, unit=None, data=None), |  | ||||||
|     'st': status(value='1970-01-01T00:00:28Z', unit=None, data=None), |  | ||||||
|     'mndt': status(value='2022-01-01', unit=None, data=None), |  | ||||||
|     'mnfv': status(value='HW-Q935BWWB-1010.0', unit=None, data=None), |  | ||||||
|     'mnhw': status(value='', unit=None, data=None), |  | ||||||
|     'di': status(value='##############################', unit=None, data=None), |  | ||||||
|     'mnsl': status(value=None, unit=None, data=None), |  | ||||||
|     'dmv': status(value='res.1.1.0,sh.1.1.0', unit=None, data=None), |  | ||||||
|     'n': status(value='Samsung Soundbar Q935B', unit=None, data=None), |  | ||||||
|     'mnmo': status(value='HW-Q935B', unit=None, data=None), |  | ||||||
|     'vid': status(value='VD-NetworkAudio-002S', unit=None, data=None), |  | ||||||
|     'mnmn': status(value='Samsung Electronics', unit=None, data=None), |  | ||||||
|     'mnml': status(value=None, unit=None, data=None), |  | ||||||
|     'mnpv': status(value='6.5', unit=None, data=None), |  | ||||||
|     'mnos': status(value='Tizen', unit=None, data=None), |  | ||||||
|     'pi': status(value='##################################', unit=None, data=None), |  | ||||||
|     'icv': status(value='core.1.1.0', unit=None, data=None), |  | ||||||
|     'mute': status(value='unmuted', unit=None, data=None), |  | ||||||
|     'totalTime': status(value=174590, unit=None, data=None), |  | ||||||
|     'audioTrackData': status(value={'title': 'QUIET', 'artist': 'ELEVATION RHYTHM', 'album': ''}, unit=None, data=None), |  | ||||||
|     'elapsedTime': status(value=28601, unit=None, data=None) |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| It is possible to fetch the current status (on/off) or information about the input. |  | ||||||
| and if Spotify / AirPlay or Bluetooth are used, also the `title` and `artist` of a played track. |  | ||||||
| 
 |  | ||||||
| All of these states can also be set. Eg. the input, volume, mute and more. |  | ||||||
|  | @ -1,234 +0,0 @@ | ||||||
| # Additional information |  | ||||||
| 
 |  | ||||||
| It is possible to retrieve even more information / control more aspects of |  | ||||||
| your Samsung soundbar, by utilizing the (undocumented) execute status. |  | ||||||
| 
 |  | ||||||
| As the [API states](https://developer.smartthings.com/docs/api/public/#operation/executeDeviceCommands),  |  | ||||||
| it is possible to execute custom commands. You can retrieve the status / values of your |  | ||||||
| custom command in the `data` attribute when fetching new information of the device. |  | ||||||
| 
 |  | ||||||
| <details> |  | ||||||
| <summary>Expand to see a sample of the fetched data of a soundbar device</summary> |  | ||||||
| 
 |  | ||||||
| This is a dictionary fetched by a `pysmartthings.device.status.attributes` after a `device.status.refresh()`. |  | ||||||
| 
 |  | ||||||
| ```python |  | ||||||
| { |  | ||||||
|     'supportedPlaybackCommands': status(value=['play', 'pause', 'stop'], unit=None, data=None), |  | ||||||
|     'playbackStatus': status(value='paused', unit=None, data=None), |  | ||||||
|     'mode': status(value=10, unit=None, data=None), |  | ||||||
|     'detailName': status(value='TV', unit=None, data=None), |  | ||||||
|     'volume': status(value=16, unit='%', data=None), |  | ||||||
|     'supportedInputSources': status(value=['digital', 'HDMI1', 'bluetooth', 'wifi'], unit=None, data=None), |  | ||||||
|     'inputSource': status(value='digital', unit=None, data=None), |  | ||||||
|     'data': status( |  | ||||||
|         value={ |  | ||||||
|             'payload': { |  | ||||||
|                 'rt': ['x.com.samsung.networkaudio.eq'], |  | ||||||
|                 'if': ['oic.if.rw', 'oic.if.baseline'], |  | ||||||
|                 'x.com.samsung.networkaudio.supportedList': ['NONE', 'POP', 'JAZZ', 'CLASSIC', 'CUSTOM'], |  | ||||||
|                 'x.com.samsung.networkaudio.EQname': 'NONE', |  | ||||||
|                 'x.com.samsung.networkaudio.action': 'setEQmode', |  | ||||||
|                 'x.com.samsung.networkaudio.EQband': ['0', '0', '0', '0', '0', '0', '0'] |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         unit=None, |  | ||||||
|         data={'href': '/sec/networkaudio/eq'} |  | ||||||
|     ), |  | ||||||
|     'switch': status(value='on', unit=None, data=None), |  | ||||||
|     'role': status(value=None, unit=None, data=None), |  | ||||||
|     'channel': status(value=None, unit=None, data=None), |  | ||||||
|     'masterName': status(value=None, unit=None, data=None), |  | ||||||
|     'status': status(value=None, unit=None, data=None), |  | ||||||
|     'st': status(value='1970-01-01T00:00:28Z', unit=None, data=None), |  | ||||||
|     'mndt': status(value='2022-01-01', unit=None, data=None), |  | ||||||
|     'mnfv': status(value='HW-Q935BWWB-1010.0', unit=None, data=None), |  | ||||||
|     'mnhw': status(value='', unit=None, data=None), |  | ||||||
|     'di': status(value='##############################', unit=None, data=None), |  | ||||||
|     'mnsl': status(value=None, unit=None, data=None), |  | ||||||
|     'dmv': status(value='res.1.1.0,sh.1.1.0', unit=None, data=None), |  | ||||||
|     'n': status(value='Samsung Soundbar Q935B', unit=None, data=None), |  | ||||||
|     'mnmo': status(value='HW-Q935B', unit=None, data=None), |  | ||||||
|     'vid': status(value='VD-NetworkAudio-002S', unit=None, data=None), |  | ||||||
|     'mnmn': status(value='Samsung Electronics', unit=None, data=None), |  | ||||||
|     'mnml': status(value=None, unit=None, data=None), |  | ||||||
|     'mnpv': status(value='6.5', unit=None, data=None), |  | ||||||
|     'mnos': status(value='Tizen', unit=None, data=None), |  | ||||||
|     'pi': status(value='##################################', unit=None, data=None), |  | ||||||
|     'icv': status(value='core.1.1.0', unit=None, data=None), |  | ||||||
|     'mute': status(value='unmuted', unit=None, data=None), |  | ||||||
|     'totalTime': status(value=174590, unit=None, data=None), |  | ||||||
|     'audioTrackData': status(value={'title': 'QUIET', 'artist': 'ELEVATION RHYTHM', 'album': ''}, unit=None, data=None), |  | ||||||
|     'elapsedTime': status(value=28601, unit=None, data=None) |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| </details> |  | ||||||
| 
 |  | ||||||
| The `data` attribute can also be fetched separately with an undocumented API endpoint. |  | ||||||
| ```python |  | ||||||
|     url = f"https://api.smartthings.com/v1/devices/{self._device_id}/components/main/capabilities/execute/status" |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| It seems that the normal `device.status.refresh()` retrieves cached results from the execute status. Therefore |  | ||||||
| using this endpoint separately seems to be a better solution. |  | ||||||
| 
 |  | ||||||
| To set the status of a given setting a command needs to be issued with the following (sample) structure: |  | ||||||
| 
 |  | ||||||
| ```python |  | ||||||
| data = { |  | ||||||
|             "commands": [ |  | ||||||
|                 { |  | ||||||
|                     "component": component_id, |  | ||||||
|                     "capability": capability, |  | ||||||
|                     "command": command, |  | ||||||
|                     "arguments": ["/sec/networkaudio/advancedaudio"] |  | ||||||
|                 } |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
| ``` |  | ||||||
| To set a setting, you will "update" an object in the given path, with a payload |  | ||||||
| similar to the following: |  | ||||||
| ```python |  | ||||||
| data = { |  | ||||||
|             "commands": [ |  | ||||||
|                 { |  | ||||||
|                     "component": component_id, |  | ||||||
|                     "capability": capability, |  | ||||||
|                     "command": command, |  | ||||||
|                     "arguments": ["/sec/networkaudio/advancedaudio", {"x.com.samsung.networkaudio.bassboost": 1}] |  | ||||||
|                 } |  | ||||||
|             ] |  | ||||||
|         } |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ## Soundmode |  | ||||||
| 
 |  | ||||||
| This setting has the href: `"/sec/networkaudio/soundmode"` |  | ||||||
| 
 |  | ||||||
| <details> |  | ||||||
| <summary> |  | ||||||
| A sample status looks like this: |  | ||||||
| </summary> |  | ||||||
| 
 |  | ||||||
| ```python |  | ||||||
| { |  | ||||||
|     'data': { |  | ||||||
|         'value': { |  | ||||||
|             'payload': { |  | ||||||
|                 'rt': ['x.com.samsung.networkaudio.soundmode'], |  | ||||||
|                 'if': ['oic.if.a', 'oic.if.baseline'], |  | ||||||
|                 'x.com.samsung.networkaudio.soundmode': 'adaptive sound', |  | ||||||
|                 'x.com.samsung.networkaudio.supportedSoundmode': ['standard', 'surround', 'game', 'adaptive sound'] |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         'data': {'href': '/sec/networkaudio/soundmode'}, |  | ||||||
|         'timestamp': '2023-09-05T14:59:50.581Z' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| </details> |  | ||||||
| 
 |  | ||||||
| ## Advanced Audio |  | ||||||
| 
 |  | ||||||
| This setting has the href: `"/sec/networkaudio/advancedaudio"` |  | ||||||
| 
 |  | ||||||
| <details> |  | ||||||
| <summary> |  | ||||||
| A sample status looks like this: |  | ||||||
| </summary> |  | ||||||
| 
 |  | ||||||
| ```python |  | ||||||
| { |  | ||||||
|     'data': { |  | ||||||
|         'value': { |  | ||||||
|             'payload': { |  | ||||||
|                 'rt': ['x.com.samsung.networkaudio.advancedaudio'], |  | ||||||
|                 'if': ['oic.if.rw', 'oic.if.baseline'], |  | ||||||
|                 'x.com.samsung.networkaudio.voiceamplifier': 0, |  | ||||||
|                 'x.com.samsung.networkaudio.bassboost': 0, |  | ||||||
|                 'x.com.samsung.networkaudio.nightmode': 0 |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         'data': {'href': '/sec/networkaudio/advancedaudio'}, |  | ||||||
|         'timestamp': '2023-09-05T15:00:14.665Z' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| </details> |  | ||||||
| 
 |  | ||||||
| ## Subwoofer |  | ||||||
| 
 |  | ||||||
| This setting has the href: `"/sec/networkaudio/woofer"` |  | ||||||
| 
 |  | ||||||
| <details> |  | ||||||
| <summary> |  | ||||||
| A sample status looks like this: |  | ||||||
| </summary> |  | ||||||
| 
 |  | ||||||
| ```python |  | ||||||
| { |  | ||||||
|     'value': { |  | ||||||
|         'payload': { |  | ||||||
|             'rt': ['x.com.samsung.networkaudio.woofer'], |  | ||||||
|             'if': ['oic.if.a', 'oic.if.baseline'], |  | ||||||
|             'x.com.samsung.networkaudio.woofer': 3, |  | ||||||
|             'x.com.samsung.networkaudio.connection': 'on' |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     'data': {'href': '/sec/networkaudio/woofer'}, |  | ||||||
|     'timestamp': '2023-09-05T14:57:36.450Z' |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| </details> |  | ||||||
| 
 |  | ||||||
| ## Equalizer |  | ||||||
| 
 |  | ||||||
| This setting has the href: `"/sec/networkaudio/eq"` |  | ||||||
| 
 |  | ||||||
| <details> |  | ||||||
| <summary> |  | ||||||
| A sample status looks like this: |  | ||||||
| </summary> |  | ||||||
| 
 |  | ||||||
| ```python |  | ||||||
| { |  | ||||||
|     'data': { |  | ||||||
|         'value': { |  | ||||||
|             'payload': { |  | ||||||
|                 'rt': ['x.com.samsung.networkaudio.eq'], |  | ||||||
|                 'if': ['oic.if.rw', 'oic.if.baseline'], |  | ||||||
|                 'x.com.samsung.networkaudio.supportedList': ['NONE', 'POP', 'JAZZ', 'CLASSIC', 'CUSTOM'], |  | ||||||
|                 'x.com.samsung.networkaudio.EQname': 'NONE', |  | ||||||
|                 'x.com.samsung.networkaudio.action': 'setEQmode', |  | ||||||
|                 'x.com.samsung.networkaudio.EQband': ['0', '0', '0', '0', '0', '0', '0'] |  | ||||||
|             } |  | ||||||
|         }, |  | ||||||
|         'data': {'href': '/sec/networkaudio/eq'}, |  | ||||||
|         'timestamp': '2023-09-05T14:59:03.490Z' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| </details> |  | ||||||
| 
 |  | ||||||
| ## Volume |  | ||||||
| 
 |  | ||||||
| This setting has the href: `"/sec/networkaudio/audio"` |  | ||||||
| 
 |  | ||||||
| <details> |  | ||||||
| <summary> |  | ||||||
| A sample status looks like this: |  | ||||||
| </summary> |  | ||||||
| 
 |  | ||||||
| ```python |  | ||||||
| { |  | ||||||
|     'data': { |  | ||||||
|         'value': {'payload': {'rt': ['oic.r.audio'], 'if': ['oic.if.a', 'oic.if.baseline'], 'mute': False, 'volume': 3}}, |  | ||||||
|         'data': {'href': '/sec/networkaudio/audio'}, |  | ||||||
|         'timestamp': '2023-09-05T15:09:04.980Z' |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
| </details> |  | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| export default defineNuxtConfig({ |  | ||||||
|   // https://github.com/nuxt-themes/docus
 |  | ||||||
|   extends: '@nuxt-themes/docus', |  | ||||||
|   modules: [ |  | ||||||
|     // https://github.com/nuxt-modules/plausible
 |  | ||||||
|     '@nuxtjs/plausible', |  | ||||||
|     // https://github.com/nuxt/devtools
 |  | ||||||
|     '@nuxt/devtools' |  | ||||||
|   ] |  | ||||||
| }) |  | ||||||
|  | @ -1,21 +0,0 @@ | ||||||
| { |  | ||||||
|   "name": "YASSI", |  | ||||||
|   "version": "0.1.0", |  | ||||||
|   "private": true, |  | ||||||
|   "scripts": { |  | ||||||
|     "dev": "nuxi dev", |  | ||||||
|     "build": "nuxi build", |  | ||||||
|     "generate": "nuxi generate", |  | ||||||
|     "preview": "nuxi preview", |  | ||||||
|     "lint": "eslint ." |  | ||||||
|   }, |  | ||||||
|   "devDependencies": { |  | ||||||
|     "@nuxt-themes/docus": "^1.13.1", |  | ||||||
|     "@nuxt/devtools": "^0.6.7", |  | ||||||
|     "@nuxt/eslint-config": "^0.1.1", |  | ||||||
|     "@nuxtjs/plausible": "^0.2.1", |  | ||||||
|     "@types/node": "^20.4.0", |  | ||||||
|     "eslint": "^8.44.0", |  | ||||||
|     "nuxt": "^3.6.2" |  | ||||||
|   } |  | ||||||
| } |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 15 KiB | 
|  | @ -1,8 +0,0 @@ | ||||||
| { |  | ||||||
|     "extends": [ |  | ||||||
|       "@nuxtjs" |  | ||||||
|     ], |  | ||||||
|     "lockFileMaintenance": { |  | ||||||
|       "enabled": true |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| import { defineTheme } from 'pinceau' |  | ||||||
| 
 |  | ||||||
| export default defineTheme({ |  | ||||||
| }) |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| { |  | ||||||
|   "extends": "./.nuxt/tsconfig.json" |  | ||||||
| } |  | ||||||
							
								
								
									
										8966
									
								
								docs/yarn.lock
								
								
								
								
							
							
						
						
									
										8966
									
								
								docs/yarn.lock
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -2,6 +2,5 @@ | ||||||
|   "name": "Samsung Soundbar", |   "name": "Samsung Soundbar", | ||||||
|   "filename": "samsung_soundbar.zip", |   "filename": "samsung_soundbar.zip", | ||||||
|   "render_readme": true, |   "render_readme": true, | ||||||
|   "zip_release": true, |   "zip_release": true | ||||||
|   "homeassistant": "2024.3.0" |  | ||||||
| } | } | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| #!/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 |  | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| #!/usr/bin/env bash |  | ||||||
| 
 |  | ||||||
| set -e |  | ||||||
| 
 |  | ||||||
| cd "$(dirname "$0")/.." |  | ||||||
| 
 |  | ||||||
| pip install rich pysmartthings |  | ||||||
		Loading…
	
		Reference in New Issue