✨ various improvements (#5)
- Added various improvements and new features. - First version of a documentation --------- Co-authored-by: samuelspagl <samuel@spagl-media.de>
This commit is contained in:
		
							parent
							
								
									b7ff6d1eb0
								
							
						
					
					
						commit
						f93019dd68
					
				|  | @ -40,8 +40,9 @@ are not documented... ;) | ||||||
|   - bass level |   - bass level | ||||||
|   - *[to come] equalizer bands* |   - *[to come] equalizer bands* | ||||||
| - `select` entity | - `select` entity | ||||||
|   - *[to come] sound mode* (additional control in the "Device" tab) |   - sound mode (additional control in the "Device" tab) | ||||||
|   - *[to come] equalizer preset* |   - input (additional control in the "Device" tab) | ||||||
|  |   - equalizer preset | ||||||
| 
 | 
 | ||||||
| ## How to install it: | ## How to install it: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -18,7 +18,7 @@ from .models import DeviceConfig, SoundbarConfig | ||||||
| 
 | 
 | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| PLATFORMS = ["media_player", "switch", "image", "number"] | PLATFORMS = ["media_player", "switch", "image", "number", "select"] | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||||||
|  |  | ||||||
|  | @ -1,9 +1,13 @@ | ||||||
|  | import asyncio | ||||||
|  | import datetime | ||||||
| import json | import json | ||||||
| import time | import time | ||||||
| from urllib.parse import quote | from urllib.parse import quote | ||||||
| 
 | import logging | ||||||
| from pysmartthings import DeviceEntity | from pysmartthings import DeviceEntity | ||||||
| 
 | 
 | ||||||
|  | from ..const import DOMAIN | ||||||
|  | log = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| class SoundbarDevice: | class SoundbarDevice: | ||||||
|     def __init__( |     def __init__( | ||||||
|  | @ -33,6 +37,7 @@ 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 | ||||||
|  | @ -55,13 +60,24 @@ 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(0.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(0.2) | ||||||
|  |             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" | ||||||
|         ] |         ] | ||||||
|  | @ -69,13 +85,31 @@ 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" | ||||||
|  | @ -85,7 +119,18 @@ 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"] | ||||||
|  | @ -132,7 +177,10 @@ class SoundbarDevice: | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def volume_level(self) -> float: |     def volume_level(self) -> float: | ||||||
|         return ((self.device.status.volume / 100) * self.__max_volume) / 100 |         vol = self.device.status.volume | ||||||
|  |         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: | ||||||
|  | @ -144,7 +192,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)) |         await self.device.set_volume(int(volume * self.__max_volume), True) | ||||||
| 
 | 
 | ||||||
|     async def mute_volume(self, mute: bool): |     async def mute_volume(self, mute: bool): | ||||||
|         if mute: |         if mute: | ||||||
|  | @ -174,6 +222,7 @@ 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 ------------- | ||||||
| 
 | 
 | ||||||
|  | @ -304,6 +353,10 @@ 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 | ||||||
|  | 
 | ||||||
|     # ------------ SUPPORT FUNCTIONS ------------ |     # ------------ SUPPORT FUNCTIONS ------------ | ||||||
| 
 | 
 | ||||||
|     async def update_execution_data(self, argument: str): |     async def update_execution_data(self, argument: str): | ||||||
|  |  | ||||||
|  | @ -1,8 +1,10 @@ | ||||||
| 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 .models import DeviceConfig | from .models import DeviceConfig | ||||||
| from .api_extension.SoundbarDevice import SoundbarDevice | from .api_extension.SoundbarDevice import SoundbarDevice | ||||||
|  | @ -41,9 +43,22 @@ class SoundbarImageEntity(ImageEntity): | ||||||
|             sw_version=self.__device.firmware_version, |             sw_version=self.__device.firmware_version, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         self._attr_image_url = self.__device.media_coverart_url |         self.__updated = None | ||||||
| 
 | 
 | ||||||
|     # ---------- 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,4 +1,5 @@ | ||||||
| import logging | import logging | ||||||
|  | from typing import Mapping, Any | ||||||
| 
 | 
 | ||||||
| from homeassistant.components.media_player import ( | from homeassistant.components.media_player import ( | ||||||
|     DEVICE_CLASS_SPEAKER, |     DEVICE_CLASS_SPEAKER, | ||||||
|  | @ -189,3 +190,7 @@ class SmartThingsSoundbarMediaPlayer(MediaPlayerEntity): | ||||||
| 
 | 
 | ||||||
|     async def async_media_stop(self): |     async def async_media_stop(self): | ||||||
|         await self.device.media_stop() |         await self.device.media_stop() | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def extra_state_attributes(self) -> Mapping[str, Any] | None: | ||||||
|  |         return self.device.retrieve_data | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import logging | import logging | ||||||
| 
 | 
 | ||||||
| from homeassistant.components.number import NumberEntity | from homeassistant.components.number import NumberEntity, NumberEntityDescription, NumberMode | ||||||
| from homeassistant.helpers.entity import DeviceInfo | from homeassistant.helpers.entity import DeviceInfo | ||||||
| 
 | 
 | ||||||
| from .models import DeviceConfig | from .models import DeviceConfig | ||||||
|  | @ -19,29 +19,29 @@ async def async_setup_entry(hass, config_entry, async_add_entities): | ||||||
|         device = device_config.device |         device = device_config.device | ||||||
|         if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID): |         if device.device_id == config_entry.data.get(CONF_ENTRY_DEVICE_ID): | ||||||
|             entities.append( |             entities.append( | ||||||
|                 SoundbarNumberEntity( |                 SoundbarWooferNumberEntity( | ||||||
|                     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 SoundbarNumberEntity(NumberEntity): | class SoundbarWooferNumberEntity(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( | ||||||
|  | @ -51,27 +51,19 @@ class SoundbarNumberEntity(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.__device.device_name |         return self.__append_unique_id | ||||||
| 
 | 
 | ||||||
|     # ------ STATE FUNCTIONS -------- |     # ------ STATE FUNCTIONS -------- | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def native_value(self) -> float | None: |     def native_value(self) -> float | None: | ||||||
|         return self.__current_value_function |         return self.__device.woofer_level | ||||||
| 
 | 
 | ||||||
|     async def async_set_native_value(self, value: float): |     async def async_set_native_value(self, value: float): | ||||||
|         if value > self.__max_value: |         await self.__device.set_woofer(int(value)) | ||||||
|             value = self.__min_value |  | ||||||
|         if value < self.__min_value: |  | ||||||
|             value = self.__min_value |  | ||||||
|         await self.__set_value_function(value) |  | ||||||
|  |  | ||||||
|  | @ -0,0 +1,163 @@ | ||||||
|  | import logging | ||||||
|  | 
 | ||||||
|  | from homeassistant.components.number import NumberEntity, NumberEntityDescription, NumberMode | ||||||
|  | from homeassistant.components.select import SelectEntityDescription, SelectEntity | ||||||
|  | from homeassistant.helpers.entity import DeviceInfo | ||||||
|  | 
 | ||||||
|  | from .models import DeviceConfig | ||||||
|  | from .api_extension.SoundbarDevice import SoundbarDevice | ||||||
|  | from .const import CONF_ENTRY_DEVICE_ID, DOMAIN | ||||||
|  | 
 | ||||||
|  | _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( | ||||||
|  |                 EqPresetSelectEntity( | ||||||
|  |                     device, | ||||||
|  |                     "eq_preset", | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             entities.append( | ||||||
|  |                 SoundModeSelectEntity( | ||||||
|  |                     device, | ||||||
|  |                     "sound_mode_preset", | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             entities.append( | ||||||
|  |                 InputSelectEntity( | ||||||
|  |                     device, | ||||||
|  |                     "input_preset", | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |     async_add_entities(entities) | ||||||
|  |     return True | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class EqPresetSelectEntity(SelectEntity): | ||||||
|  |     def __init__( | ||||||
|  |             self, | ||||||
|  |             device: SoundbarDevice, | ||||||
|  |             append_unique_id: str, | ||||||
|  |     ): | ||||||
|  |         self.entity_id = f"number.{device.device_name}_{append_unique_id}" | ||||||
|  |         self.entity_description = SelectEntityDescription(key=append_unique_id, | ||||||
|  |                                                           ) | ||||||
|  |         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 | ||||||
|  | 
 | ||||||
|  |     # ------ 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, | ||||||
|  |     ): | ||||||
|  |         self.entity_id = f"number.{device.device_name}_{append_unique_id}" | ||||||
|  |         self.entity_description = SelectEntityDescription(key=append_unique_id, | ||||||
|  |                                                           ) | ||||||
|  |         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 | ||||||
|  | 
 | ||||||
|  |     # ------ 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, | ||||||
|  |     ): | ||||||
|  |         self.entity_id = f"number.{device.device_name}_{append_unique_id}" | ||||||
|  |         self.entity_description = SelectEntityDescription(key=append_unique_id, | ||||||
|  |                                                           ) | ||||||
|  |         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 | ||||||
|  | 
 | ||||||
|  |     # ------ 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) | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | dist | ||||||
|  | node_modules | ||||||
|  | .output | ||||||
|  | .nuxt | ||||||
|  | @ -0,0 +1,8 @@ | ||||||
|  | module.exports = { | ||||||
|  |   root: true, | ||||||
|  |   extends: '@nuxt/eslint-config', | ||||||
|  |   rules: { | ||||||
|  |     'vue/max-attributes-per-line': 'off', | ||||||
|  |     'vue/multi-word-component-names': 'off' | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,12 @@ | ||||||
|  | node_modules | ||||||
|  | *.iml | ||||||
|  | .idea | ||||||
|  | *.log* | ||||||
|  | .nuxt | ||||||
|  | .vscode | ||||||
|  | .DS_Store | ||||||
|  | coverage | ||||||
|  | dist | ||||||
|  | sw.* | ||||||
|  | .env | ||||||
|  | .output | ||||||
|  | @ -0,0 +1,2 @@ | ||||||
|  | shamefully-hoist=true | ||||||
|  | strict-peer-dependencies=false | ||||||
|  | @ -0,0 +1,57 @@ | ||||||
|  | # 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). | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | export default defineAppConfig({ | ||||||
|  |   docus: { | ||||||
|  |     title: 'YASSI', | ||||||
|  |     description: 'HomeAssistant: Yet another Samsung soundbar integration', | ||||||
|  |     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: true, | ||||||
|  |       showLinkIcon: true, | ||||||
|  |       exclude: [], | ||||||
|  |       fluid: true | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | }) | ||||||
|  | @ -0,0 +1,69 @@ | ||||||
|  | --- | ||||||
|  | title: "YASSI" | ||||||
|  | --- | ||||||
|  | ::block-hero | ||||||
|  | --- | ||||||
|  | cta: | ||||||
|  |   - Why another HomeAssistant integration? | ||||||
|  |   - #why-another-integration | ||||||
|  | secondary: | ||||||
|  |   - Open on GitHub → | ||||||
|  |   - https://github.com/nuxtlabs/docus | ||||||
|  | snippet: | ||||||
|  |   - Custom Components | ||||||
|  |   - "- input selection" | ||||||
|  |   - "- soundmode selection" | ||||||
|  |   - "- eq-preset selection" | ||||||
|  |   - "- woofer settings" | ||||||
|  |   - "- other cool things" | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | #title | ||||||
|  | Yassi | ||||||
|  | 
 | ||||||
|  | #description | ||||||
|  | Yet another Samsung soundbar integration for HomeAssistant | ||||||
|  | :: | ||||||
|  | 
 | ||||||
|  | ::card-grid | ||||||
|  | #title | ||||||
|  | Quick-Start | ||||||
|  | #root | ||||||
|  | :ellipsis | ||||||
|  | 
 | ||||||
|  | #default | ||||||
|  |   ::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="/getting-started"} | ||||||
|  |   :: | ||||||
|  | 
 | ||||||
|  |   ::card | ||||||
|  |   #title | ||||||
|  |   Features | ||||||
|  |   #description | ||||||
|  |   Many cool features are awaiting your eyes to see ✨. | ||||||
|  |   <br> | ||||||
|  |   <br> | ||||||
|  |   :button-link[click here]{href="/features"} | ||||||
|  |   :: | ||||||
|  | :: | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## Why another integration | ||||||
|  | 
 | ||||||
|  | The current Samsung Soundbar Integration by @PiotrMachowski / @thierryBourbon are already pretty cool. | ||||||
|  | But I wanted it to appear as a device, and base the Foundation on the `pysmartthings` python package. | ||||||
|  | 
 | ||||||
|  | Additionally, I wanted full control over the *Soundmode* and more. So I tried out a few things with the API, | ||||||
|  | and found that also the **Subwoofer** as well as the **Equalizer** are controllable. | ||||||
|  | 
 | ||||||
|  | I created a new wrapper around the `pysmartthings.DeviceEntity` specifically set up for a Soundbar, and this | ||||||
|  | is the Result. | ||||||
|  | 
 | ||||||
|  | I hope to integrate also controls for **surround speaker** as well as **Space-Fit Sound**, but as these features | ||||||
|  | are not documented... ;)  | ||||||
|  | @ -0,0 +1,30 @@ | ||||||
|  | # Getting Started | ||||||
|  | 
 | ||||||
|  | ## Installation | ||||||
|  | 
 | ||||||
|  | ### HACS (official) | ||||||
|  | 
 | ||||||
|  | > ⚠️ Not done yet, hopefully soon. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ### HACS (custom repository) | ||||||
|  | 
 | ||||||
|  | You can add this repository as a custom repository to your hacs. | ||||||
|  | After you've done that, you can search for it like with the "official" | ||||||
|  | integrations. | ||||||
|  | 
 | ||||||
|  | ### Manual | ||||||
|  | 
 | ||||||
|  | Copy the contents of `custom_components/samsung_soundbar` to `config/custom_components/samsung_soundbar` | ||||||
|  | on your HomeAssistant instance. | ||||||
|  | 
 | ||||||
|  | ## Configuration | ||||||
|  | 
 | ||||||
|  | After you installed the custom component, it should be possible to configure the integration | ||||||
|  | in the `device` settings of your HomeAssistant. | ||||||
|  | 
 | ||||||
|  | You will need: | ||||||
|  | - a SmartThings `api_key` [click here](https://account.smartthings.com/tokens) | ||||||
|  | - the `device_id` of your device [click here](https://my.smartthings.com/advanced/devices) | ||||||
|  | - a name for your Soundbar | ||||||
|  | - and a `max_volume` | ||||||
|  | @ -0,0 +1,35 @@ | ||||||
|  | # Features | ||||||
|  | 
 | ||||||
|  | **YASSI** and retrieve / set the status of the following features grouped as a device: | ||||||
|  | - `media_player`: | ||||||
|  |   - `on / off` [*read, write*] | ||||||
|  |   - `volume` (set, step) [*read, write*] | ||||||
|  |   - `input` (select) [*read*, write*] | ||||||
|  |   - `sound_mode` (select) [*read, write*] | ||||||
|  |   - `play` (button) [*write*] | ||||||
|  |   - `pause` (button) [*write*] | ||||||
|  |   - `media_artwork` (image) [*read*] | ||||||
|  |   - `media_title` (text) [*read*] | ||||||
|  |   - `media_artist` (text) [*read*] | ||||||
|  | 
 | ||||||
|  | - `number` | ||||||
|  |   - **Woofer** | ||||||
|  |     - level (set) [*read, write*] | ||||||
|  | - `select` | ||||||
|  |   - **Input** | ||||||
|  |     - `input` [*read, write*] | ||||||
|  |     - `supported_inputs` [*read*] | ||||||
|  |   - **Soundmode** | ||||||
|  |     - `active_soundmode` [*read, write*] | ||||||
|  |     - `supported_soundmodes` [*read*] | ||||||
|  |   - **EQ-Preset** | ||||||
|  |     - `active_eq_preset` [*read, write*] | ||||||
|  |     - `supported_eq_preset` [*read*] | ||||||
|  | 
 | ||||||
|  | - `button` | ||||||
|  |   - `night_mode` [*read, write*] | ||||||
|  |   - `voice_amplifier` [*read, write*] | ||||||
|  |   - `bass_mode` [*read, write*] | ||||||
|  | 
 | ||||||
|  | - `image` | ||||||
|  |   - `media_coverart` [*read*] | ||||||
|  | @ -0,0 +1,47 @@ | ||||||
|  | # "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. | ||||||
|  | @ -0,0 +1,234 @@ | ||||||
|  | # 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> | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  | export default defineNuxtConfig({ | ||||||
|  |   // https://github.com/nuxt-themes/docus
 | ||||||
|  |   extends: '@nuxt-themes/docus', | ||||||
|  |   app: { | ||||||
|  |     baseURL: '/ha_samsung_soundbar/', | ||||||
|  |     }, | ||||||
|  |   modules: [ | ||||||
|  |     // https://github.com/nuxt-modules/plausible
 | ||||||
|  |     '@nuxtjs/plausible', | ||||||
|  |     // https://github.com/nuxt/devtools
 | ||||||
|  |     '@nuxt/devtools' | ||||||
|  |   ] | ||||||
|  | }) | ||||||
|  | @ -0,0 +1,21 @@ | ||||||
|  | { | ||||||
|  |   "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.
										
									
								
							| After Width: | Height: | Size: 15 KiB | 
|  | @ -0,0 +1,8 @@ | ||||||
|  | { | ||||||
|  |     "extends": [ | ||||||
|  |       "@nuxtjs" | ||||||
|  |     ], | ||||||
|  |     "lockFileMaintenance": { | ||||||
|  |       "enabled": true | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,4 @@ | ||||||
|  | import { defineTheme } from 'pinceau' | ||||||
|  | 
 | ||||||
|  | export default defineTheme({ | ||||||
|  | }) | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | { | ||||||
|  |   "extends": "./.nuxt/tsconfig.json" | ||||||
|  | } | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | class Stuff: | ||||||
|  |     def __init__(self, a: int): | ||||||
|  |         self.__a = a | ||||||
|  | 
 | ||||||
|  |     def set_a(self, new_a: int): | ||||||
|  |         self.__a = new_a | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def a(self): | ||||||
|  |         return self.__a | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Stuff2: | ||||||
|  |     def __init__(self, a: int): | ||||||
|  |         self.stuff = Stuff(a) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     def set_a(self, a): | ||||||
|  |         self.stuff.set_a(a) | ||||||
|  | 
 | ||||||
|  |     @property | ||||||
|  |     def a(self): | ||||||
|  |         return self.stuff.a | ||||||
|  | 
 | ||||||
|  | hal = Stuff(3) | ||||||
|  | print(hal.a) | ||||||
|  | hal.set_a(5) | ||||||
|  | print(hal.a) | ||||||
|  | print() | ||||||
|  | print() | ||||||
|  | has = Stuff2(3) | ||||||
|  | print(has.a) | ||||||
|  | print(has.stuff.set_a(5)) | ||||||
|  | print(has.a) | ||||||
|  | print(has.set_a(10)) | ||||||
|  | print(has.a) | ||||||
		Loading…
	
		Reference in New Issue