Skip to content

Instantly share code, notes, and snippets.

@c-kick
Last active March 11, 2026 01:14
Show Gist options
  • Select an option

  • Save c-kick/e15c233262e0050dd3f56a6070525862 to your computer and use it in GitHub Desktop.

Select an option

Save c-kick/e15c233262e0050dd3f56a6070525862 to your computer and use it in GitHub Desktop.
Samsung soundbar control via SmartThings cloud API
#!/usr/bin/env python3
"""Samsung soundbar control via SmartThings cloud API.
Controls advanced audio features (night mode, voice amplifier, bass boost)
that are NOT available through the local JSON-RPC API on port 1516.
Works by reading the SmartThings OAuth token from Home Assistant's config
storage, then calling the SmartThings execute capability with OCF resource
paths. No external dependencies — stdlib only.
Tested on: HW-Q930D (firmware SAT-MT8532D24WWC-1030.9)
Should work on: other 2024 Samsung soundbars (Q990D, Q800D, S800D, etc.)
Setup:
1. Soundbar must be added to SmartThings
2. HA SmartThings integration must be configured (provides the OAuth token)
3. Set DEVICE_ID below to your soundbar's SmartThings device ID
(find it via: https://api.smartthings.com/v1/devices with your token,
or in the SmartThings app under device info)
4. Place this script in /config/scripts/ (HA container path)
5. Add shell_commands to configuration.yaml:
shell_command:
soundbar_nightmode_on: "python3 /config/scripts/soundbar_control.py set nightmode 1"
soundbar_nightmode_off: "python3 /config/scripts/soundbar_control.py set nightmode 0"
soundbar_voiceamp_on: "python3 /config/scripts/soundbar_control.py set voiceamplifier 1"
soundbar_voiceamp_off: "python3 /config/scripts/soundbar_control.py set voiceamplifier 0"
soundbar_bassboost_on: "python3 /config/scripts/soundbar_control.py set bassboost 1"
soundbar_bassboost_off: "python3 /config/scripts/soundbar_control.py set bassboost 0"
6. Add template switches:
template:
- switch:
- name: "Soundbar Night Mode"
unique_id: soundbar_nightmode
icon: mdi:weather-night
state: "{{ this.state | default('off') }}"
turn_on:
- action: shell_command.soundbar_nightmode_on
turn_off:
- action: shell_command.soundbar_nightmode_off
7. Reload Shell Commands + Template Entities from Developer Tools > YAML
Usage:
soundbar_control.py set <setting> <0|1>
soundbar_control.py get <setting>
Settings: nightmode, voiceamplifier, bassboost
Note: commands work even when the soundbar is powered off — the setting
is applied via the SmartThings cloud and takes effect on next power-on.
"""
import json
import sys
import time
import urllib.request
import ssl
STORAGE_PATH = "/config/.storage/core.config_entries"
DEVICE_ID = "cd8511a0-80f5-698d-1084-f21e6a1bf2f5"
ST_API = "https://api.smartthings.com/v1"
SETTINGS_MAP = {
"nightmode": "x.com.samsung.networkaudio.nightmode",
"voiceamplifier": "x.com.samsung.networkaudio.voiceamplifier",
"bassboost": "x.com.samsung.networkaudio.bassboost",
}
def get_smartthings_token():
with open(STORAGE_PATH) as f:
data = json.load(f)
for entry in data.get("data", {}).get("entries", []):
if entry.get("domain") == "smartthings":
return entry["data"]["token"]["access_token"]
raise RuntimeError("SmartThings config entry not found")
def st_request(token, path, body=None):
url = f"{ST_API}{path}"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
}
data = json.dumps(body).encode() if body else None
method = "POST" if body else "GET"
ctx = ssl.create_default_context()
for attempt in range(5):
try:
req = urllib.request.Request(url, data=data, headers=headers, method=method)
with urllib.request.urlopen(req, context=ctx, timeout=15) as resp:
return json.loads(resp.read())
except urllib.error.URLError:
if attempt < 4:
time.sleep(2)
continue
raise
def set_setting(token, setting, value):
param = SETTINGS_MAP[setting]
body = {
"commands": [{
"component": "main",
"capability": "execute",
"command": "execute",
"arguments": [
"/sec/networkaudio/advancedaudio",
{param: int(value)},
],
}]
}
result = st_request(token, f"/devices/{DEVICE_ID}/commands", body)
status = result.get("results", [{}])[0].get("status", "UNKNOWN")
print(json.dumps({"setting": setting, "value": int(value), "status": status}))
return status == "COMPLETED"
def get_settings(token):
"""Read advanced audio settings. Triggers a read then fetches status."""
# Trigger read
body = {
"commands": [{
"component": "main",
"capability": "execute",
"command": "execute",
"arguments": ["/sec/networkaudio/advancedaudio"],
}]
}
st_request(token, f"/devices/{DEVICE_ID}/commands", body)
# Fetch result
import time
time.sleep(1)
result = st_request(
token,
f"/devices/{DEVICE_ID}/components/main/capabilities/execute/status",
)
data = result.get("data", {}).get("value", {})
if data and isinstance(data, dict):
payload = data.get("payload", {})
print(json.dumps(payload, indent=2))
else:
print(json.dumps({"error": "no data returned", "raw": data}))
def main():
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} get|set <setting> [value]", file=sys.stderr)
sys.exit(1)
action = sys.argv[1]
setting = sys.argv[2].lower()
if setting not in SETTINGS_MAP and action == "set":
print(f"Unknown setting: {setting}. Use: {', '.join(SETTINGS_MAP)}", file=sys.stderr)
sys.exit(1)
token = get_smartthings_token()
if action == "set":
if len(sys.argv) < 4:
print("Usage: set <setting> <0|1>", file=sys.stderr)
sys.exit(1)
value = int(sys.argv[3])
if not set_setting(token, setting, value):
sys.exit(1)
elif action == "get":
get_settings(token)
else:
print(f"Unknown action: {action}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
@sirivanleo
Copy link

thanks this worked out pretty well for the night mode

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment