Async library to control Samsung consumer TVs over RS232
Project description
samsung-exlink
Async Python library to control Samsung consumer TVs (Frame, Q-series, 8000-series, etc.) over their RS232 serial connection -- which Samsung markets as ExLink (also written EX-Link / EXT Link) -- built on serialx.
"ExLink" and "RS232" refer to the same thing here: the TV's serial control port. This library speaks that protocol; it does not use the network/WiFi APIs.
Installation
pip install samsung-exlink
Requires Python 3.12+.
Hardware
Samsung consumer TVs expose RS232 either via:
- A native 3.5 mm "ExLink" jack (Q70 and above) -- plug in and go.
- 3.5 mm pin-out: tip = TV TX (RX into your controller), ring = TV RX, sleeve = GND.
- A USB port via the proprietary Samsung USB-to-ExLink dongle (8000 / Q60).
- The TV's service menu (Mute -> 1 -> 8 -> 2 -> Power on the IR remote) must have EXT Link Support and USB Serial enabled.
The serial link is 9600 8N1.
Quick start
import asyncio
from samsung_exlink import SamsungTV, InputSource, Key, PictureMode
async def main():
tv = SamsungTV("/dev/ttyUSB0")
await tv.connect()
try:
await tv.power_on()
await tv.set_volume(25)
await tv.select_input_source(InputSource.HDMI1)
await tv.set_picture_mode(PictureMode.MOVIE)
await tv.send_key(Key.KEY_MENU)
finally:
await tv.disconnect()
asyncio.run(main())
The constructor also accepts an esphome://host/?port_name=NAME URL for use
with an ESPHome UART proxy.
CLI
A built-in CLI lets you quickly test your connection:
python -m samsung_exlink /dev/ttyUSB0 power-on
python -m samsung_exlink /dev/ttyUSB0 power-off
python -m samsung_exlink /dev/ttyUSB0 volume 25
python -m samsung_exlink /dev/ttyUSB0 volume-up
python -m samsung_exlink /dev/ttyUSB0 volume-down
python -m samsung_exlink /dev/ttyUSB0 mute
python -m samsung_exlink /dev/ttyUSB0 channel-up
python -m samsung_exlink /dev/ttyUSB0 channel-down
python -m samsung_exlink /dev/ttyUSB0 source HDMI1
python -m samsung_exlink /dev/ttyUSB0 key KEY_MENU
python -m samsung_exlink /dev/ttyUSB0 raw 0d 00 00 1a # MENU via raw command
python -m samsung_exlink /dev/ttyUSB0 status # query power/volume/mute/source/channel
python -m samsung_exlink /dev/ttyUSB0 listen # passively log frames
The status command queries the TV directly. To translate the raw source
byte into a named input, pass a known generation with --model (one of
frame_2022, h_series_2016):
python -m samsung_exlink --model frame_2022 /dev/ttyUSB0 status
Protocol
Samsung consumer TVs use a fixed 7-byte binary frame:
08 22 [cmd1] [cmd2] [cmd3] [value] [checksum]
08 22is the fixed header.cmd1,cmd2,cmd3,valueform the command (see the protocol PDF bundled with this repo).checksum = (256 - sum(bytes 0..5)) & 0xFF.
The TV replies with a 3-byte response: 03 0C F1 for ACK, 03 0C FF for
NACK. Most commands are fire-and-forget, so the library also tracks state
from what it sends. A subset of state (power, volume, mute, channel, source)
can additionally be read back with a cmd1=0xF0 status query -- the TV
answers with 03 0C F5 plus a 10-byte payload. See
State tracking below.
Examples
| Operation | Frame |
|---|---|
| Power off | 08 22 00 00 00 01 D5 |
| Power on | 08 22 00 00 00 02 D4 |
| Volume = 25 | 08 22 01 00 00 19 BC |
| Mute toggle | 08 22 02 00 00 00 D4 |
| Source: HDMI1 | 08 22 0A 00 05 00 C7 |
| Source: TV | 08 22 0A 00 00 00 CC |
| Key: MENU | 08 22 0D 00 00 1A AF |
Features
State tracking
state = tv.state
state.power # bool | None (None = unknown)
state.volume # int | None (0-100)
state.mute # bool | None
state.input_source # InputSource | None
state.picture_mode # PictureMode | None
state.sound_mode # SoundMode | None
Fields are populated as commands succeed, and also refreshed by the query_*
methods below. Power state is unknown after connect; call power_on() /
power_off(), or query_power() to read it from the TV.
Status queries
Some TVs answer a small set of status queries directly:
await tv.query_power() # PowerState.ON / STANDBY / OFF
await tv.query_volume() # 0-100
await tv.query_mute() # bool
await tv.query_channel() # (major, mid, minor)
await tv.query_source() # raw source byte (generation-specific)
await tv.query_source_input() # InputSource | None (needs a source map)
Each query updates tv.state and returns the value. The byte returned by
query_source() differs by TV generation, so mapping it back to an
InputSource needs a source map. Supply a known one via the constructor:
from samsung_exlink import SamsungTV, MODELS
tv = SamsungTV("/dev/ttyUSB0", model=MODELS["frame_2022"])
or discover it for your TV once with await tv.probe_sources() (which
cycles every input and records the byte each reports) and pass the returned
mapping back as source_map=... on future runs. Queries raise TimeoutError
on TVs/firmwares that don't support them.
Event subscription
def on_state_change(state):
if state is None:
print("Disconnected")
return
print(f"Volume: {state.volume}, Source: {state.input_source}")
unsubscribe = tv.subscribe(on_state_change)
The callback is called with a TVState snapshot on each change, or None
when the connection is torn down.
Raw access
response = await tv.send_raw(0x0d, 0x00, 0x00, 0x1A) # KEY_MENU
# response is the 3-byte reply (e.g. b"\x03\x0c\xf1")
send_raw returns the reply without raising on NACK, for diagnostics.
Available commands
First-class helpers cover power (power_on/power_off/power_toggle),
volume (set_volume/volume_up/volume_down), mute, channels
(channel_up/channel_down), select_input_source, set_picture_mode,
set_sound_mode, and the Frame/Anynet+ toggles set_art_mode,
set_ambient_mode, and set_hdmi_cec.
The Key enum mirrors the Samsung remote-control key map and includes
power, source, navigation, transport, color, and number keys. The
InputSource enum covers TV, AV1-3, S-Video1-3, Component1-3, PC1-3,
HDMI1-4, DVI1-3, and RVU.
For commands not yet wrapped (e.g. white-balance, color space, screen
adjustments), use send_raw() with the values from the protocol PDF
(docs/Samsung-RS232-Control.pdf).
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file samsung_exlink-1.0.0.tar.gz.
File metadata
- Download URL: samsung_exlink-1.0.0.tar.gz
- Upload date:
- Size: 15.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44c096ac7e56ecaa214b4dfc55999884ce3e8abf8743641fd336eb3ceb1c20bd
|
|
| MD5 |
2c9f598dacdc97e2f050fb2ec5df06e8
|
|
| BLAKE2b-256 |
ef2dff5d56de77d71bd96c71e00785dd948be6a1b2faa5d06f8c1a8118b64d6e
|
Provenance
The following attestation bundles were made for samsung_exlink-1.0.0.tar.gz:
Publisher:
publish.yml on home-assistant-libs/samsung-exlink
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
samsung_exlink-1.0.0.tar.gz -
Subject digest:
44c096ac7e56ecaa214b4dfc55999884ce3e8abf8743641fd336eb3ceb1c20bd - Sigstore transparency entry: 1825900552
- Sigstore integration time:
-
Permalink:
home-assistant-libs/samsung-exlink@fb21ea481ed32c18c8d36778c9b325a58c3f353a -
Branch / Tag:
refs/tags/1.0.0 - Owner: https://github.com/home-assistant-libs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fb21ea481ed32c18c8d36778c9b325a58c3f353a -
Trigger Event:
release
-
Statement type:
File details
Details for the file samsung_exlink-1.0.0-py3-none-any.whl.
File metadata
- Download URL: samsung_exlink-1.0.0-py3-none-any.whl
- Upload date:
- Size: 18.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
379e32865a327c776093c7d09e0fc84cf9adde031fa0cdf740e66d42469aa38a
|
|
| MD5 |
54bbe532ada5c730061b8ec9a6ff9194
|
|
| BLAKE2b-256 |
fdfa73d48e5e00d49a4705ba05518fa18d6567a1a830d60a9af8821ad826cd97
|
Provenance
The following attestation bundles were made for samsung_exlink-1.0.0-py3-none-any.whl:
Publisher:
publish.yml on home-assistant-libs/samsung-exlink
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
samsung_exlink-1.0.0-py3-none-any.whl -
Subject digest:
379e32865a327c776093c7d09e0fc84cf9adde031fa0cdf740e66d42469aa38a - Sigstore transparency entry: 1825900568
- Sigstore integration time:
-
Permalink:
home-assistant-libs/samsung-exlink@fb21ea481ed32c18c8d36778c9b325a58c3f353a -
Branch / Tag:
refs/tags/1.0.0 - Owner: https://github.com/home-assistant-libs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fb21ea481ed32c18c8d36778c9b325a58c3f353a -
Trigger Event:
release
-
Statement type: