HappySCCM

/

/

How to permanently disable the standby LED on a Samsung UA50JU6400 (2015 J-series) – Thx Claude


This guide disables the red standby LED entirely via software. No hardware modification, no opening the TV, no soldering. The LED stays off through standby and power cycles.

Tested on: UA50JU6400WXXY, firmware T-HKMUABC 1550.0 (build 2019-11-28), Tizen 2.3.0. May work on other 2015 J-series Samsung TVs with the same firmware platform.

Prerequisites

  • A computer on the same local network as the TV
  • Python 3 installed on the computer
  • The TV’s IP address (find it in TV Settings → Network → Network Status)

Step 1: Enable Developer Mode on the TV

  1. On the TV remote, press Smart Hub (or Home)
  2. Navigate to Apps
  3. In the Apps panel, press 1 2 3 4 5 on the remote (a settings dialog appears)
  4. Toggle Developer Mode to ON
  5. Enter your computer’s local IP address (e.g. 192.168.1.100) — find it with ifconfig (Mac) or ipconfig (Windows)
  6. Press OK and accept the prompt
  7. Restart the TV (power off completely, then back on)

After restart, Developer Mode is active and the SDB (Samsung Debug Bridge) service listens on TCP port 26101.

Reference: https://developer.samsung.com/smarttv/develop/getting-started/using-sdk/tv-device.html

Step 2: Verify connectivity

From your computer’s terminal:

python3 -c "
import socket
try:
    s = socket.create_connection(('TV_IP_HERE', 26101), timeout=5)
    s.close()
    print('OK - TV is reachable on SDB port')
except Exception as e:
    print(f'FAIL - {e}')
"

Replace TV_IP_HERE with the TV’s actual IP. If it says “Connection refused” or “timed out”, check that:

  • The TV is powered ON (not in standby)
  • Developer Mode is enabled
  • Your computer’s IP matches what you entered in the Developer Mode setup

Step 3: Run the LED disable script

Save the following as disable_led.py and run it with python3 disable_led.py:

"""
Permanently disable the standby LED on Samsung JU6400 (2015 J-series).

This script:
1. Uploads a LED-off Python script to the TV's persistent storage
2. Connects to systemd via the TV's private DBUS socket
3. Installs a systemd service that runs at every boot
4. Sends the LED-off MICOM commands immediately

The root filesystem is temporarily remounted read-write for the service
installation, then re-sealed read-only.
"""
import socket, struct, time, sys

# ============================================================
# CONFIGURATION — change this to your TV's IP address
# ============================================================
TV_IP = 'TV_IP_HERE'
TV_PORT = 26101
# ============================================================

MAX_PAYLOAD = 4096

def exact(sock, count):
    buf = bytearray()
    while len(buf) < count:
        chunk = sock.recv(count - len(buf))
        if not chunk:
            raise EOFError('Connection closed')
        buf.extend(chunk)
    return bytes(buf)

def send_pkt(sock, cmd, a0, a1, payload=b''):
    n = int.from_bytes(cmd, 'little')
    sock.sendall(struct.pack('<6I', n, a0, a1, len(payload),
                             sum(payload), n ^ 0xffffffff) + payload)

def recv_pkt(sock, timeout=10):
    sock.settimeout(timeout)
    cmd, a0, a1, length, csum, magic = struct.unpack('<6I', exact(sock, 24))
    if length > MAX_PAYLOAD or magic != cmd ^ 0xffffffff:
        raise ValueError('Invalid packet')
    payload = exact(sock, length) if length else b''
    return cmd.to_bytes(4, 'little'), a0, a1, payload

def read_dbus_lines(sock, lid, rid, timeout=3):
    buf = bytearray()
    deadline = time.time() + timeout
    while time.time() < deadline:
        try:
            c, _, _, p = recv_pkt(sock, max(0.5, deadline - time.time()))
            if c == b'OKAY':
                continue
            if c == b'WRTE':
                buf.extend(p)
                send_pkt(sock, b'OKAY', lid, rid)
            if c == b'CLSE':
                break
        except (socket.timeout, OSError):
            break
    return bytes(buf)

def read_dbus_binary(sock, lid, rid, timeout=10):
    buf = bytearray()
    deadline = time.time() + timeout
    while time.time() < deadline:
        try:
            c, _, _, p = recv_pkt(sock, max(0.5, deadline - time.time()))
            if c == b'OKAY':
                continue
            if c == b'WRTE':
                buf.extend(p)
                send_pkt(sock, b'OKAY', lid, rid)
            if c == b'CLSE':
                break
        except (socket.timeout, OSError):
            break
    return bytes(buf)

class DBusWriter:
    def __init__(self):
        self.buf = bytearray()

    def _pad(self, a):
        r = len(self.buf) % a
        if r:
            self.buf += b'\0' * (a - r)

    def write_boolean(self, v):
        self._pad(4)
        self.buf += struct.pack('<I', 1 if v else 0)

    def write_string(self, s):
        self._pad(4)
        b = s.encode('utf-8')
        self.buf += struct.pack('<I', len(b)) + b + b'\0'

    def start_array(self, element_alignment):
        self._pad(4)
        length_pos = len(self.buf)
        self.buf += b'\0\0\0\0'
        self._pad(element_alignment)
        content_start = len(self.buf)
        return length_pos, content_start

    def end_array(self, length_pos, content_start):
        array_len = len(self.buf) - content_start
        struct.pack_into('<I', self.buf, length_pos, array_len)

    def data(self):
        return bytes(self.buf)

def build_method_call(serial, path, member, interface=None, sig=None, body=b''):
    fields = bytearray()

    def add_field(code, type_sig, value_bytes):
        pad = (8 - (len(fields) % 8)) % 8
        fields.extend(b'\0' * pad)
        fields.append(code)
        fields.append(len(type_sig))
        fields.extend(type_sig.encode('ascii'))
        fields.append(0)
        fields.extend(value_bytes)

    def str_bytes(s):
        b = s.encode('utf-8')
        return struct.pack('<I', len(b)) + b + b'\0'

    def sig_bytes(s):
        b = s.encode('ascii')
        return bytes([len(b)]) + b + b'\0'

    add_field(1, 'o', str_bytes(path))
    if interface:
        add_field(2, 's', str_bytes(interface))
    add_field(3, 's', str_bytes(member))
    if sig:
        add_field(8, 'g', sig_bytes(sig))
    fields_bytes = bytes(fields)
    hdr_pad = (8 - (len(fields_bytes) % 8)) % 8
    header = struct.pack('<BBBBIII', ord('l'), 1, 0, 1,
                         len(body), serial, len(fields_bytes))
    return header + fields_bytes + b'\0' * hdr_pad + body

def sdb_send_file(path, content, mode=0o644):
    """Upload a file to the TV via SDB SEND."""
    sock = socket.create_connection((TV_IP, TV_PORT), timeout=10)
    sock.settimeout(10)
    send_pkt(sock, b'CNXN', 0x01000000, MAX_PAYLOAD, b'host::\0')
    recv_pkt(sock)
    send_pkt(sock, b'OPEN', 1, 0, b'sync:\0')
    cmd, rid, _, _ = recv_pkt(sock)
    if cmd != b'OKAY':
        sock.close()
        return False
    buf = bytearray()

    def _w(data):
        for off in range(0, len(data), MAX_PAYLOAD):
            send_pkt(sock, b'WRTE', 1, rid, data[off:off + MAX_PAYLOAD])
            while True:
                c, _, _, p = recv_pkt(sock)
                if c == b'OKAY':
                    break
                if c == b'WRTE':
                    buf.extend(p)
                    send_pkt(sock, b'OKAY', 1, rid)
                if c == b'CLSE':
                    raise EOFError

    def _r(n):
        while len(buf) < n:
            c, _, _, p = recv_pkt(sock)
            if c == b'WRTE':
                buf.extend(p)
                send_pkt(sock, b'OKAY', 1, rid)
            elif c == b'CLSE':
                raise EOFError
        r = bytes(buf[:n])
        del buf[:n]
        return r

    ps = f'{path},{mode}'.encode()
    _w(b'SEND' + struct.pack('<I', len(ps)) + ps)
    cm = MAX_PAYLOAD - 64
    for off in range(0, len(content), cm):
        chunk = content[off:off + cm]
        _w(b'DATA' + struct.pack('<I', len(chunk)) + chunk)
    _w(b'DONE' + struct.pack('<I', int(time.time())))
    tag = _r(4)
    _r(4)
    send_pkt(sock, b'CLSE', 1, rid)
    sock.close()
    return tag == b'OKAY'

def sdb_stat(path):
    """Check if a file exists on the TV via SDB STAT."""
    sock = socket.create_connection((TV_IP, TV_PORT), timeout=10)
    sock.settimeout(10)
    send_pkt(sock, b'CNXN', 0x01000000, MAX_PAYLOAD, b'host::\0')
    recv_pkt(sock)
    send_pkt(sock, b'OPEN', 1, 0, b'sync:\0')
    cmd, rid, _, _ = recv_pkt(sock)
    if cmd != b'OKAY':
        sock.close()
        return 0, 0

    buf = bytearray()

    def _w(data):
        for off in range(0, len(data), MAX_PAYLOAD):
            send_pkt(sock, b'WRTE', 1, rid, data[off:off + MAX_PAYLOAD])
            while True:
                c, _, _, p = recv_pkt(sock)
                if c == b'OKAY':
                    break
                if c == b'WRTE':
                    buf.extend(p)
                    send_pkt(sock, b'OKAY', 1, rid)
                if c == b'CLSE':
                    raise EOFError

    def _r(n):
        while len(buf) < n:
            c, _, _, p = recv_pkt(sock)
            if c == b'WRTE':
                buf.extend(p)
                send_pkt(sock, b'OKAY', 1, rid)
            elif c == b'CLSE':
                raise EOFError
        r = bytes(buf[:n])
        del buf[:n]
        return r

    name = path.encode()
    _w(b'STAT' + struct.pack('<I', len(name)) + name)
    tag = _r(4)
    if tag == b'STAT':
        m, s, t = struct.unpack_from('<3I', _r(12))
        send_pkt(sock, b'CLSE', 1, rid)
        sock.close()
        return m, s
    send_pkt(sock, b'CLSE', 1, rid)
    sock.close()
    return 0, 0

# ============================================================
# Files to install on the TV
# ============================================================

# This script runs at every boot via systemd — just sends LED-off commands
LED_OFF_RUN = b"""\
import os, array, fcntl
try:
    fd = os.open('/dev/micom-msg', os.O_RDWR)
    msg = array.array('B', [0xE5, 0xE5] + [0]*10)
    fcntl.ioctl(fd, 0xC00CB301, msg, True)
    os.close(fd)
except:
    pass
try:
    fd = os.open('/dev/micom-msg', os.O_RDWR)
    msg2 = array.array('B', [0x2A, 0x3A] + [0]*10)
    fcntl.ioctl(fd, 0xC00CB301, msg2, True)
    os.close(fd)
except:
    pass
"""

# One-time installer: remounts /, writes service file, creates symlink, LED off
INSTALLER = b"""\
import os, array, fcntl, errno

results = []

ret = os.system('mount -o remount,rw /')
results.append('remount_rw:' + str(ret))

if ret == 0:
    svc = '[Unit]\\nDescription=LED Off\\nAfter=deviced-pre.service\\n\\n[Service]\\nType=oneshot\\nExecStart=/usr/bin/python /home/developer/led-off-run.py\\n\\n[Install]\\nWantedBy=multi-user.target\\n'
    try:
        f = open('/etc/systemd/system/led-off.service', 'w')
        f.write(svc)
        f.close()
        results.append('svc:ok')
    except Exception as e:
        results.append('svc:' + str(e))

    wdir = '/etc/systemd/system/multi-user.target.wants'
    try:
        os.makedirs(wdir)
    except OSError:
        pass

    wlink = wdir + '/led-off.service'
    try:
        if os.path.exists(wlink):
            os.unlink(wlink)
        os.symlink('/etc/systemd/system/led-off.service', wlink)
        results.append('link:ok')
    except Exception as e:
        results.append('link:' + str(e))

    results.append('svc_exists:' + str(os.path.exists('/etc/systemd/system/led-off.service')))
    results.append('link_exists:' + str(os.path.islink(wlink)))

    ret2 = os.system('mount -o remount,ro /')
    results.append('remount_ro:' + str(ret2))

try:
    fd = os.open('/dev/micom-msg', os.O_RDWR)
    msg = array.array('B', [0xE5, 0xE5] + [0]*10)
    fcntl.ioctl(fd, 0xC00CB301, msg, True)
    os.close(fd)
    results.append('e5:ok')
except Exception as e:
    results.append('e5:' + str(e))

try:
    fd = os.open('/dev/micom-msg', os.O_RDWR)
    msg2 = array.array('B', [0x2A, 0x3A] + [0]*10)
    fcntl.ioctl(fd, 0xC00CB301, msg2, True)
    os.close(fd)
    results.append('2a:ok')
except Exception as e:
    results.append('2a:' + str(e))

f = open('/tmp/led-install-result', 'w')
f.write('\\n'.join(results))
f.close()
"""

INSTALLER_SERVICE = b"""\
[Unit]
Description=LED Off Installer

[Service]
Type=oneshot
ExecStart=/usr/bin/python /tmp/led-installer.py
"""

# ============================================================
# Main script
# ============================================================

def main():
    if TV_IP == 'TV_IP_HERE':
        print('ERROR: Edit this script and set TV_IP to your TV\'s IP address.')
        sys.exit(1)

    print(f'Samsung JU6400 LED Disable')
    print(f'TV: {TV_IP}:{TV_PORT}')
    print()

    # Step 1: Upload files
    print('[1/4] Uploading files to TV...')
    ok1 = sdb_send_file('/tmp/led-installer.py', INSTALLER)
    print(f'  Installer script: {"OK" if ok1 else "FAIL"}')
    ok2 = sdb_send_file('/home/developer/led-off-run.py', LED_OFF_RUN)
    print(f'  Boot-time script: {"OK" if ok2 else "FAIL"}')
    ok3 = sdb_send_file('/tmp/led-installer.service', INSTALLER_SERVICE)
    print(f'  Service file:     {"OK" if ok3 else "FAIL"}')

    if not (ok1 and ok2 and ok3):
        print('\nUpload failed. Check TV connectivity and Developer Mode.')
        sys.exit(1)

    # Step 2: Connect to systemd via DBUS
    print('\n[2/4] Connecting to systemd via DBUS...')
    sock = socket.create_connection((TV_IP, TV_PORT), timeout=10)
    sock.settimeout(10)
    lid = 1
    send_pkt(sock, b'CNXN', 0x01000000, MAX_PAYLOAD, b'host::\0')
    recv_pkt(sock)
    send_pkt(sock, b'OPEN', lid, 0, b'localfilesystem:/run/systemd/private\0')
    cmd, rid, _, _ = recv_pkt(sock)
    if cmd != b'OKAY':
        print('  Failed to open systemd socket')
        sys.exit(1)
    print('  Socket opened')

    send_pkt(sock, b'WRTE', lid, rid, b'\x00AUTH EXTERNAL 30\r\n')
    resp = read_dbus_lines(sock, lid, rid, 3)
    if b'DATA' in resp:
        send_pkt(sock, b'WRTE', lid, rid, b'DATA\r\n')
        read_dbus_lines(sock, lid, rid, 3)
    send_pkt(sock, b'WRTE', lid, rid, b'BEGIN\r\n')
    time.sleep(0.3)
    print('  Authenticated')

    # Step 3: Load, link, reload, start the installer service
    print('\n[3/4] Running installer via systemd...')
    serial = 1

    # LoadUnit
    w = DBusWriter()
    w.write_string('led-installer.service')
    msg = build_method_call(serial, '/org/freedesktop/systemd1', 'LoadUnit',
                            interface='org.freedesktop.systemd1.Manager',
                            sig='s', body=w.data())
    serial += 1
    send_pkt(sock, b'WRTE', lid, rid, msg)
    resp = read_dbus_binary(sock, lid, rid, 5)
    if resp and resp[1] == 4:
        read_dbus_binary(sock, lid, rid, 3)
    print('  LoadUnit: done')

    # LinkUnitFiles
    w = DBusWriter()
    lp, cs = w.start_array(4)
    w.write_string('/tmp/led-installer.service')
    w.end_array(lp, cs)
    w.write_boolean(True)
    w.write_boolean(True)
    msg = build_method_call(serial, '/org/freedesktop/systemd1', 'LinkUnitFiles',
                            interface='org.freedesktop.systemd1.Manager',
                            sig='asbb', body=w.data())
    serial += 1
    send_pkt(sock, b'WRTE', lid, rid, msg)
    resp = read_dbus_binary(sock, lid, rid, 5)
    if resp and resp[1] == 4:
        read_dbus_binary(sock, lid, rid, 3)
    print('  LinkUnitFiles: done')

    # Reload
    msg = build_method_call(serial, '/org/freedesktop/systemd1', 'Reload',
                            interface='org.freedesktop.systemd1.Manager')
    serial += 1
    send_pkt(sock, b'WRTE', lid, rid, msg)
    resp = read_dbus_binary(sock, lid, rid, 10)
    if resp and resp[1] == 4:
        read_dbus_binary(sock, lid, rid, 5)
    print('  Reload: done')

    # StartUnit
    w = DBusWriter()
    w.write_string('led-installer.service')
    w.write_string('replace')
    msg = build_method_call(serial, '/org/freedesktop/systemd1', 'StartUnit',
                            interface='org.freedesktop.systemd1.Manager',
                            sig='ss', body=w.data())
    serial += 1
    send_pkt(sock, b'WRTE', lid, rid, msg)
    resp = read_dbus_binary(sock, lid, rid, 10)
    if resp and resp[1] == 4:
        read_dbus_binary(sock, lid, rid, 5)
    print('  StartUnit: done')

    send_pkt(sock, b'CLSE', lid, rid)
    sock.close()

    # Step 4: Wait and verify
    print('\n[4/4] Waiting for installer to complete...')
    time.sleep(10)

    svc_m, _ = sdb_stat('/etc/systemd/system/led-off.service')
    want_m, _ = sdb_stat('/etc/systemd/system/multi-user.target.wants/led-off.service')
    run_m, _ = sdb_stat('/home/developer/led-off-run.py')
    result_m, result_sz = sdb_stat('/tmp/led-install-result')

    print()
    if svc_m and want_m and run_m:
        print('SUCCESS! LED disable service installed permanently.')
        print()
        print('  Service:  /etc/systemd/system/led-off.service')
        print('  Symlink:  /etc/systemd/system/multi-user.target.wants/led-off.service')
        print('  Script:   /home/developer/led-off-run.py')
        print()
        print('The LED should already be off. Power cycle the TV to confirm')
        print('the LED stays off through standby and restarts.')
    elif result_m:
        print(f'Installer ran (result file: {result_sz} bytes) but service')
        print('files not found. The remount may have failed.')
    else:
        print('Installer may not have executed. Check:')
        print('  - TV is powered on (not standby)')
        print('  - Developer Mode is enabled')
        print('  - Your IP is registered in Developer Mode settings')

if __name__ == '__main__':
    main()

How it works

The TV’s normal service menu LED setting (Option → Engineer Option → Standby led on/off = OFF) doesn’t actually suppress the LED on this model. The shutdown path re-reads the factory setting and sends a MICOM command regardless.

This script bypasses that by:

  1. Uploading files via SDB (Samsung Debug Bridge) file transfer
  2. Connecting to systemd’s private DBUS socket through SDB’s localfilesystem: transport — this reaches systemd (PID 1), which runs as root with full privileges
  3. Installing a systemd service that runs at every boot, before the normal TV UI starts
  4. The service executes a Python script that sends two MICOM ioctl commands:
    • 0xE5, 0xE5 — standby LED off
    • 0x2A, 0x3A — power LED off

The root filesystem (/etc/systemd/system/) is normally read-only, so the installer temporarily remounts it read-write, writes the service file and symlink, then remounts read-only.

Reversal

To re-enable the LED, run this script on the same computer with the TV on and Developer Mode active. Save as reenable_led.py:

"""Re-enable the standby LED by removing the LED-off service."""
# Copy all the helper functions from disable_led.py above, then:

UNDO_SCRIPT = b"""\
import os
os.system('mount -o remount,rw /')
try:
    os.unlink('/etc/systemd/system/multi-user.target.wants/led-off.service')
except:
    pass
try:
    os.unlink('/etc/systemd/system/led-off.service')
except:
    pass
os.system('mount -o remount,ro /')
open('/tmp/led-undo-result', 'w').write('done')
"""

# Upload and run via the same systemd DBUS method as the installer.
# After a reboot, the LED will return to its default behavior.

Troubleshooting

“Connection refused” on port 26101:

  • TV must be powered ON, not in standby
  • Developer Mode must be enabled (press 12345 in Apps)
  • Your computer’s IP must match the one entered in Developer Mode settings
  • Restart the TV after enabling Developer Mode

Installer runs but service files not created:

  • The root filesystem remount may have failed. This is model/firmware dependent.
  • Check that /tmp/led-install-result exists (the script ran) vs not (the script didn’t execute)

LED comes back on after firmware update:

  • A firmware update may overwrite /etc/systemd/system/. Re-run the installer after updating.

Works on power cycle but not on standby-wake:

  • The E5/E5 command suppresses the standby LED. If it comes back during certain sleep/wake transitions, the boot-time command should re-suppress it on next full boot.

Technical details

  • MICOM: The TV’s microcontroller that controls the LED and power management. Communication is via /dev/micom-msg (mode 0666, SMACK label *) using ioctl 0xC00CB301 with a 12-byte message.
  • systemd version 43: Very old. No StartTransientUnit (requires systemd 209+). Uses LoadUnit + LinkUnitFiles + Reload + StartUnit instead.
  • UEP bypass: The TV’s Unified Enforcement Policy blocks execution from debug-launchpad child processes. systemd (PID 1) is not subject to UEP, which is why this approach works while others don’t.
  • SDB protocol: Samsung Debug Bridge, similar to ADB. Version 0x01000000, max payload 4096. Uses localfilesystem: transport to reach Unix domain sockets on the TV.



Leave a Reply

Your email address will not be published. Required fields are marked *