Skip to content

Commit

Permalink
[ 1.0.98 ] * Added service get_audio_product_tone_controls. Gets th…
Browse files Browse the repository at this point in the history
…e current audio product tone controls configuration of the device.

  * Added service `set_audio_product_tone_controls`. Sets the current audio product tone controls configuration of the device.
  * Updated underlying `spotifywebapiPython` package requirement to version 1.0.171.
  • Loading branch information
thlucas1 committed Feb 21, 2025
1 parent 5cc1de2 commit 99d2640
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 11 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Change are listed in reverse chronological order (newest to oldest).

<span class="changelog">

###### [ 1.0.98 ] - 2025/02/21

* Added service `get_audio_product_tone_controls`. Gets the current audio product tone controls configuration of the device.
* Added service `set_audio_product_tone_controls`. Sets the current audio product tone controls configuration of the device.
* Updated underlying `spotifywebapiPython` package requirement to version 1.0.171.

###### [ 1.0.97 ] - 2025/02/20

* Added service `get_balance`. Gets the current balance configuration of the device.
Expand Down
52 changes: 50 additions & 2 deletions custom_components/soundtouchplus/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
# -----------------------------------------------------------------------------------
SERVICE_AUDIO_TONE_LEVELS = "audio_tone_levels"
SERVICE_CLEAR_SOURCE_NOWPLAYINGSTATUS = "clear_source_nowplayingstatus"
SERVICE_GET_AUDIO_PRODUCT_TONE_CONTROLS = "get_audio_product_tone_controls"
SERVICE_GET_BALANCE = "get_balance"
SERVICE_GET_BASS_CAPABILITIES = "get_bass_capabilities"
SERVICE_GET_SOURCE_LIST = "get_source_list"
Expand All @@ -88,6 +89,7 @@
SERVICE_RECENT_LIST = "recent_list"
SERVICE_RECENT_LIST_CACHE = "recent_list_cache"
SERVICE_REMOTE_KEYPRESS = "remote_keypress"
SERVICE_SET_AUDIO_PRODUCT_TONE_CONTROLS = "set_audio_product_tone_controls"
SERVICE_SET_BALANCE_LEVEL = "set_balance_level"
SERVICE_SET_BASS_LEVEL = "set_bass_level"
SERVICE_SNAPSHOT_RESTORE = "snapshot_restore"
Expand All @@ -111,6 +113,13 @@
}
)

SERVICE_GET_AUDIO_PRODUCT_TONE_CONTROLS_SCHEMA = vol.Schema(
{
vol.Required("entity_id"): cv.entity_id,
vol.Optional("refresh", default=True): cv.boolean,
}
)

SERVICE_GET_BALANCE_SCHEMA = vol.Schema(
{
vol.Required("entity_id"): cv.entity_id,
Expand Down Expand Up @@ -229,10 +238,18 @@
}
)

SERVICE_SET_AUDIO_PRODUCT_TONE_CONTROLS_SCHEMA = vol.Schema(
{
vol.Required("entity_id"): cv.entity_id,
vol.Required("bass_level", default=25): vol.All(vol.Range(min=-100,max=100)),
vol.Required("treble_level", default=50): vol.All(vol.Range(min=-100,max=100))
}
)

SERVICE_SET_BALANCE_LEVEL_SCHEMA = vol.Schema(
{
vol.Required("entity_id"): cv.entity_id,
vol.Required("level", default=-0): vol.All(vol.Range(min=-7,max=7))
vol.Required("level", default=0): vol.All(vol.Range(min=-7,max=7))
}
)

Expand Down Expand Up @@ -418,6 +435,12 @@ async def service_handle_entity(service:ServiceCall) -> None:
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name))
await hass.async_add_executor_job(entity.service_remote_keypress, key_id, key_state)

elif service.service == SERVICE_SET_AUDIO_PRODUCT_TONE_CONTROLS:
bass_level = service.data.get("bass_level")
treble_level = service.data.get("treble_level")
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name))
await hass.async_add_executor_job(entity.service_set_audio_product_tone_controls, bass_level, treble_level)

elif service.service == SERVICE_SET_BALANCE_LEVEL:
level = service.data.get("level")
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name))
Expand Down Expand Up @@ -598,7 +621,14 @@ async def service_handle_serviceresponse(service: ServiceCall) -> ServiceRespons
response:dict = {}

# process service request.
if service.service == SERVICE_GET_BALANCE:
if service.service == SERVICE_GET_AUDIO_PRODUCT_TONE_CONTROLS:

# get audio product tone controls.
refresh = service.data.get("refresh")
_logsi.LogVerbose(STAppMessages.MSG_SERVICE_EXECUTE % (service.service, entity.name))
response = await hass.async_add_executor_job(entity.service_get_audio_product_tone_controls, refresh)

elif service.service == SERVICE_GET_BALANCE:

# get balance.
refresh = service.data.get("refresh")
Expand Down Expand Up @@ -737,6 +767,15 @@ def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id:
supports_response=SupportsResponse.NONE,
)

_logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_GET_AUDIO_PRODUCT_TONE_CONTROLS, SERVICE_GET_AUDIO_PRODUCT_TONE_CONTROLS_SCHEMA)
hass.services.async_register(
DOMAIN,
SERVICE_GET_AUDIO_PRODUCT_TONE_CONTROLS,
service_handle_serviceresponse,
schema=SERVICE_GET_AUDIO_PRODUCT_TONE_CONTROLS_SCHEMA,
supports_response=SupportsResponse.ONLY,
)

_logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_GET_BALANCE, SERVICE_GET_BALANCE_SCHEMA)
hass.services.async_register(
DOMAIN,
Expand Down Expand Up @@ -863,6 +902,15 @@ def _GetEntityFromServiceData(hass:HomeAssistant, service:ServiceCall, field_id:
supports_response=SupportsResponse.NONE,
)

_logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SET_AUDIO_PRODUCT_TONE_CONTROLS, SERVICE_SET_AUDIO_PRODUCT_TONE_CONTROLS_SCHEMA)
hass.services.async_register(
DOMAIN,
SERVICE_SET_AUDIO_PRODUCT_TONE_CONTROLS,
service_handle_entity,
schema=SERVICE_SET_AUDIO_PRODUCT_TONE_CONTROLS_SCHEMA,
supports_response=SupportsResponse.NONE,
)

_logsi.LogObject(SILevel.Verbose, STAppMessages.MSG_SERVICE_REQUEST_REGISTER % SERVICE_SET_BALANCE_LEVEL, SERVICE_SET_BALANCE_LEVEL_SCHEMA)
hass.services.async_register(
DOMAIN,
Expand Down
4 changes: 2 additions & 2 deletions custom_components/soundtouchplus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
"requests>=2.31.0",
"requests_oauthlib>=1.3.1",
"smartinspectPython>=3.0.34",
"spotifywebapiPython>=1.0.170",
"spotifywebapiPython>=1.0.171",
"urllib3>=1.21.1,<1.27",
"zeroconf>=0.132.2"
],
"version": "1.0.97",
"version": "1.0.98",
"zeroconf": [ "_soundtouch._tcp.local." ]
}
117 changes: 111 additions & 6 deletions custom_components/soundtouchplus/media_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -1584,18 +1584,21 @@ def service_audio_tone_levels(
_logsi.LogWarning("'%s': MediaPlayer device does not support AudioProductToneControls; cannot change the tone levels" % self.name)
return

# get current tone levels.
# get current configuration.
config:AudioProductToneControls = self.data.client.GetAudioProductToneControls()

# set new tone control values.
# set audio product tone values.
config.Bass.Value = bassLevel
config.Treble.Value = bassLevel
config.Treble.Value = trebleLevel
self.data.client.SetAudioProductToneControls(config)

# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SoundTouchError as ex:
raise HomeAssistantError(ex.Message)
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException(None, ex)
raise IntegrationError(str(ex)) from ex

finally:

Expand Down Expand Up @@ -1646,6 +1649,54 @@ def service_clear_source_nowplayingstatus(
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def service_get_audio_product_tone_controls(
self,
refresh:bool=False,
) -> dict:
"""
Gets the current audio product tone controls configuration of the device.
Args:
refresh (bool):
True to query the device for realtime information and refresh the cache;
otherwise, False to just return the cached information.
Returns:
A `AudioProductToneControls` object dictionary that contains audio product tone control
configuration of the device IF the device supports it (e.g. ST-300, etc);
otherwise, None if the device does not support it.
Note that some SoundTouch devices do not support this functionality. For example,
the ST-300 will support this, but the ST-10 will not.
"""
apiMethodName:str = 'service_get_audio_product_tone_controls'
apiMethodParms:SIMethodParmListContext = None
result:AudioProductToneControls = None

try:

# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("refresh", refresh)
_logsi.LogMethodParmList(SILevel.Verbose, "SoundTouch Get Audio Product Tone Controls Service", apiMethodParms)

# request information from SoundTouch Web API.
result = self.data.client.GetAudioProductToneControls(refresh)

# return the result dictionary.
return result.ToDictionary()

# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SoundTouchError as ex:
raise ServiceValidationError(ex.Message)

finally:

# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def service_get_balance(
self,
refresh:bool=False,
Expand Down Expand Up @@ -2367,6 +2418,62 @@ def service_remote_keypress(
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def service_set_audio_product_tone_controls(
self,
bassLevel:int=0,
trebleLevel:int=0,
) -> None:
"""
Sets the current audio product tone controls configuration of the device.
Args:
bassLevel (int):
Bass level to set, usually in the range of -100 (low) to 100 (high).
trebleLevel (int):
Treble level to set, usually in the range of -100 (low) to 100 (high).
Raises:
SoundTouchError:
If the device is not capable of supporting `audioproducttonecontrols` functions,
as determined by a query to the cached `supportedURLs` web-services api.
If the controls argument is None, or not of type `AudioProductToneControls`.
Note that some SoundTouch devices do not support this functionality. For example,
the ST-300 will support this, but the ST-10 will not.
"""
apiMethodName:str = 'service_set_audio_product_tone_controls'
apiMethodParms:SIMethodParmListContext = None

try:

# trace.
apiMethodParms = _logsi.EnterMethodParmList(SILevel.Debug, apiMethodName)
apiMethodParms.AppendKeyValue("bassLevel", bassLevel)
apiMethodParms.AppendKeyValue("trebleLevel", trebleLevel)
_logsi.LogMethodParmList(SILevel.Verbose, "SoundTouch Set Audio Product Tone Controls Service", apiMethodParms)

# get current configuration.
config:AudioProductToneControls = self.data.client.GetAudioProductToneControls()

# set audio product tone values.
config.Bass.Value = bassLevel
config.Treble.Value = trebleLevel
self.data.client.SetAudioProductToneControls(config)

# the following exceptions have already been logged, so we just need to
# pass them back to HA for display in the log (or service UI).
except SoundTouchError as ex:
raise ServiceValidationError(ex.Message)
except Exception as ex:
_logsi.LogException(None, ex)
raise IntegrationError(str(ex)) from ex

finally:

# trace.
_logsi.LeaveMethod(SILevel.Debug, apiMethodName)


def service_set_balance_level(
self,
level:int=0,
Expand Down Expand Up @@ -2395,7 +2502,6 @@ def service_set_balance_level(
_logsi.LogMethodParmList(SILevel.Verbose, "SoundTouch Set Balance Level Service", apiMethodParms)

# set bass level.
_logsi.LogVerbose("Setting balance level")
self.data.client.SetBalanceLevel(level)

# the following exceptions have already been logged, so we just need to
Expand Down Expand Up @@ -2437,7 +2543,6 @@ def service_set_bass_level(
_logsi.LogMethodParmList(SILevel.Verbose, "SoundTouch Set Bass Level Service", apiMethodParms)

# set bass level.
_logsi.LogVerbose("Setting bass level")
self.data.client.SetBassLevel(level)

# the following exceptions have already been logged, so we just need to
Expand Down
55 changes: 55 additions & 0 deletions custom_components/soundtouchplus/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,27 @@ clear_source_nowplayingstatus:
selector:
text:

get_audio_product_tone_controls:
name: Get Audio Product Tone Controls
description: Gets the current audio product tone controls configuration of the device.
fields:
entity_id:
name: Entity ID
description: Entity ID of the SoundTouchPlus device that will process the request.
example: "media_player.soundtouch_livingroom"
required: true
selector:
entity:
integration: soundtouchplus
domain: media_player
refresh:
name: Refresh?
description: True to query the device for realtime information and refresh the cache; otherwise, False to just return the cached information.
example: "false"
required: true
selector:
boolean:

get_balance:
name: Get Balance
description: Gets the current balance configuration of the device.
Expand Down Expand Up @@ -549,6 +570,40 @@ remote_keypress:
- press
- release

set_audio_product_tone_controls:
name: Set Audio Product Tone Controls
description: Sets the current audio product tone controls configuration of the device.
fields:
entity_id:
name: Entity ID
description: Entity ID of the SoundTouchPlus device that will process the request.
example: "media_player.soundtouch_livingroom"
required: true
selector:
entity:
integration: soundtouchplus
domain: media_player
bass_level:
name: Bass Level
description: Bass level to set, usually in the range of -100 (low) to 100 (high).
example: "25"
required: true
selector:
number:
min: -100
max: 100
mode: box
treble_level:
name: Treble Level
description: Treble level to set, usually in the range of -100 (low) to 100 (high).
example: "50"
required: true
selector:
number:
min: -100
max: 100
mode: box

set_balance_level:
name: Set Balance Level
description: Sets the device balance level to the given level.
Expand Down
Loading

0 comments on commit 99d2640

Please sign in to comment.