SLWF-08 HDMI-CEC controller for ESPHome and Home Assistant

Aus aktuellem Anlass wollte ich noch mal diese alternative Möglichkeit zum Steuern von HDMI Geräten hinweisen !

Wer seinen TV in Home Assistant einbinden möchte, ohne große Umbaumaßnahmen, dem möchte ich den SMLIGHT SLWF-08 vorstellen.

Das Gerät ist ein kleiner Stick, der einfach in einen freien HDMI-Port am TV gesteckt wird. Über die sogenannte CEC-Leitung – die in jedem HDMI-Kabel steckt – kann er dann Befehle senden und empfangen. Die meisten Hersteller kennen diese Funktion unter eigenem Namen: Samsung nennt es Anynet+, LG SimpLink, Sony Bravia Sync, Philips EasyLink – dahinter steckt aber immer dasselbe Prinzip.

Der Stick verbindet sich per WLAN mit dem Heimnetzwerk und taucht dann automatisch in Home Assistant auf. Von dort aus lässt sich der TV ganz normal steuern: Ein- und Ausschalten, Lautstärke, Eingang wechseln, Navigation – alles per Automatisierung oder Dashboard.

Das Besondere: Anders als App-basierte Lösungen funktioniert CEC auch wenn der TV aus ist – der Stick kann ihn aus dem Standby aufwecken.

Kosten: ca. 13€ Voraussetzung: TV muss CEC unterstützen (bei fast allen Geräten ab 2010 der Fall) und die Funktion im TV-Menü aktiviert sein.


:light_bulb: Für alle, die ihren TV einfach und günstig in die Heimautomatisierung einbinden wollen – ein klare Empfehlung!

Was alles geht, kleines Beispiel!

8 „Gefällt mir“

Interessanter Ansatz CEC können heute ja sie meisten TV!
Ich habe das bei mir so gelöst, dass ich mir die WebUI in HA geholt habe und über dort es steuern kann!

Mein Stick ist heute angekommen. Einen kurzen Probelauf habe ich bereits gemacht. Im Gehäuse des Sticks befindet sich eine leichte Delle, deswegen war mir ein grundlegender Funktionstest wichtig. Im Netzwerk ist der Stick, aber HA hat ihn nicht erkannt. Im Webinterface konnte ich den Fernseher ausschalten, mehr war nicht möglich. Allerdings hat der Stick verhindert, dass sich die Lautstärke des AV-Receivers verstellen ließ. Da der Fernseher zu dem Zeitpunkt aber in Benutzung war, habe ich meinen ersten Versuch abgebrochen.
Das wird wahrscheinlich eine Aufgabe für morgen Vormittag. Hast irgendwelche Ratschläge wie ich Fallstricke umgehen kann?

Orginal ist da ja nichts weiter drauf! An/Aus/Laut/Leise/Mute oder so!?
Da ging bei mir nur „Ausschalten“!

Ich habe mir ne eigene angepasste (speziell für Philips) Firmware dazu gebaut. Sollte aber kein Problem sein. Was für ein TV hast du?

Einen Samsung mit TizenOS 2111. Also eigentlich ein recht neues Allerwelts Modell

Genau, wirklich nur Basics. Ich denke als erstes muss ich den Stick zu ESPhome hinzufügen und dafür sorgen das er nicht die Bedienung stört, wenn ich nicht gerade versuche das Ding zum laufen zu bekommen.

Hab mal meinen Code per “KI” an Samsung anpassen lassen, eventuell hat er es ja nicht komplett versaut! :wink:

substitutions:
  name: "hdmi-control-samsung"
  friendly_name: "HDMI Control - Samsung"
  project_name: "SMLIGHT.SLWF-08-HDMI"
  project_version: "1.2-Samsung"

esphome:
  name: "${name}"
  friendly_name: "${friendly_name}"
  name_add_mac_suffix: true
  project:
    name: "${project_name}"
    version: "${project_version}"

esp8266:
  board: esp12e

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "HDMI-AP"
    password: "slwf0800"

captive_portal:

api:
  services:
    - service: hdmi_cec_send
      variables:
        cec_destination: int
        cec_data: int[]
      then:
        - hdmi_cec.send:
            destination: !lambda "return static_cast<unsigned char>(cec_destination);"
            data: !lambda |-
              std::vector<unsigned char> charVector;
              for (int i : cec_data) {
                charVector.push_back(static_cast<unsigned char>(i));
              }
              return charVector;

ota:
  - platform: esphome

web_server:
  port: 80

logger:

external_components:
  - source: github://Palakis/esphome-hdmi-cec

dashboard_import:
  package_import_url: github://smlight-tech/slwf-08/esphome-configs/slwf-08-Palakis.yaml

# ---------------------------------------------------------------------------
# HDMI-CEC Konfiguration für Samsung (Anynet+, TizenOS)
# Voraussetzung: Anynet+ im TV-Menü aktivieren!
#   → Einstellungen → Allgemein → Externe Geräte-Manager → Anynet+ (HDMI-CEC)
#
# Unterschiede zu Philips (EasyLink):
#   - Lautstärke per Broadcast (0xF) statt direkt an TV (0x0)
#   - Kein ARC-Handler (0xC3) nötig
#   - Kein Audio-Status-Handler (0x71) nötig
# ---------------------------------------------------------------------------
hdmi_cec:
  pin: GPIO14
  address: 0x04           # 0x04 = Playback Device
  physical_address: 0x3000  # Anpassen je nach HDMI-Port! (0x1000=HDMI1, 0x2000=HDMI2, etc.)
  osd_name: "SLWF-08-HDMI"
  promiscuous_mode: true  # Alle CEC-Nachrichten mithören - wichtig!
  monitor_mode: false
  on_message:
    # --- Standby empfangen ---
    - opcode: 0x36
      then:
        logger.log: "TV geht in Standby"

    # --- Give Physical Address beantworten ---
    - opcode: 0x83
      then:
        - if:
            condition:
              lambda: 'return destination == 0x04 || destination == 0x0F;'
            then:
              - hdmi_cec.send:
                  destination: 0x0F
                  data: [0x84, 0x30, 0x00, 0x04]
                  # 0x84 = Report Physical Address
                  # 0x30, 0x00 = physische Adresse 3.0.0.0 (= 0x3000)
                  # 0x04 = Gerätetyp Playback Device

    # --- Device Vendor ID melden ---
    - opcode: 0x8C
      source: 0x0   # nur vom TV
      then:
        - hdmi_cec.send:
            destination: 0x0F
            data: [0x87, 0x00, 0x00, 0x00]
            # 0x87 = Device Vendor ID
            # 0x00, 0x00, 0x00 = Generic

    # --- OSD Name melden ---
    - opcode: 0x46
      then:
        - hdmi_cec.send:
            destination: !lambda return source;
            data: [0x47, 0x53, 0x4C, 0x57, 0x46, 0x2D, 0x30, 0x38]
            # 0x47 = Set OSD Name
            # Rest = ASCII "SLWF-08"

    # --- TV eingeschaltet ---
    - opcode: 0x04
      then:
        logger.log: "TV wurde eingeschaltet"

    # --- Menü-Request beantworten ---
    - opcode: 0x8D
      then:
        hdmi_cec.send:
          destination: !lambda return source;
          data: [0x8E, 0x01]

    # --- Power Status melden ---
    - opcode: 0x8F
      source: 0x0   # nur vom TV
      then:
        - hdmi_cec.send:
            destination: !lambda return source;
            data: [0x90, 0x00]  # 0x90 = Report Power Status, 0x00 = On

    # HINWEIS Samsung:
    #   - 0xC3 (ARC) und 0x71 (Audio Status) werden bei Samsung
    #     nicht benötigt und daher hier weggelassen.
    #   - Samsung verwaltet ARC und Audio-Status intern über Anynet+.

    # --- Alle Nachrichten als HA-Event und Sensor ---
    - then:
        - homeassistant.event:
            event: esphome.hdmi_cec
            data:
              source: !lambda 'return source;'
              destination: !lambda 'return destination;'
              opcode: !lambda 'return data.size() ? data[0] : 0;'
              translated: !lambda 'return hdmi_cec::Frame(source, destination, data).to_string();'
        - lambda: |-
            hdmi_cec::Frame frame = hdmi_cec::Frame(source, destination, data);
            id(cec_raw_message).publish_state(frame.to_string(true));
            id(cec_translated_message).publish_state(frame.to_string());

# ---------------------------------------------------------------------------
# Text-Sensoren
# ---------------------------------------------------------------------------
text_sensor:
  - platform: template
    name: "CEC Nachricht (raw)"
    id: cec_raw_message
    update_interval: never
  - platform: template
    name: "CEC Nachricht (lesbar)"
    id: cec_translated_message
    update_interval: never

# ---------------------------------------------------------------------------
# Buttons
# WICHTIG für Samsung (Anynet+):
#   - Navigations-/Wiedergabe-/Zahlentasten brauchen IMMER:
#     1. 0x44 + Tastencode  (Key Pressed)
#     2. 0x45               (Key Released)
#   - Lautstärke per BROADCAST (0xF) ← SAMSUNG-SPEZIFISCH!
#     (Philips: direkt an 0x0)
#   - HDMI-Eingang über "Active Source" Broadcast
# ---------------------------------------------------------------------------
button:

  # ==========================================================================
  # POWER
  # ==========================================================================
  - platform: template
    name: "TV einschalten"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x04]      # Image View On → direkt an TV

  - platform: template
    name: "Alles ausschalten (Standby)"
    on_press:
      - hdmi_cec.send:
          destination: 0xF  # Broadcast
          data: [0x36]      # Standby

  # ==========================================================================
  # LAUTSTÄRKE
  # SAMSUNG-SPEZIFISCH: Broadcast (0xF) statt direkt an TV (0x0)
  # Bei Philips ist es umgekehrt!
  # ==========================================================================
  - platform: template
    name: "Lautstärke Hoch"
    on_press:
      - hdmi_cec.send:
          destination: 0xF  # SAMSUNG: Broadcast
          data: [0x44, 0x41] # Key Pressed: Volume Up
      - hdmi_cec.send:
          destination: 0xF
          data: [0x45]       # Key Released

  - platform: template
    name: "Lautstärke Runter"
    on_press:
      - hdmi_cec.send:
          destination: 0xF  # SAMSUNG: Broadcast
          data: [0x44, 0x42] # Key Pressed: Volume Down
      - hdmi_cec.send:
          destination: 0xF
          data: [0x45]       # Key Released

  - platform: template
    name: "Stummschalten"
    on_press:
      - hdmi_cec.send:
          destination: 0xF  # SAMSUNG: Broadcast
          data: [0x44, 0x43] # Key Pressed: Mute
      - hdmi_cec.send:
          destination: 0xF
          data: [0x45]       # Key Released

  # ==========================================================================
  # EINGANG / QUELLE
  # ==========================================================================
  - platform: template
    name: "HDMI 1 wählen"
    on_press:
      - hdmi_cec.send:
          destination: 0xF
          data: [0x82, 0x10, 0x00] # Active Source: HDMI 1 (0x1000)

  - platform: template
    name: "HDMI 2 wählen"
    on_press:
      - hdmi_cec.send:
          destination: 0xF
          data: [0x82, 0x20, 0x00] # Active Source: HDMI 2 (0x2000)

  - platform: template
    name: "HDMI 3 wählen"
    on_press:
      - hdmi_cec.send:
          destination: 0xF
          data: [0x82, 0x30, 0x00] # Active Source: HDMI 3 (0x3000)

  - platform: template
    name: "HDMI 4 wählen"
    on_press:
      - hdmi_cec.send:
          destination: 0xF
          data: [0x82, 0x40, 0x00] # Active Source: HDMI 4 (0x4000)

  # ==========================================================================
  # NAVIGATION
  # ==========================================================================
  - platform: template
    name: "Menü öffnen"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x09] # Root Menu
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Pfeil Hoch"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x01]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Pfeil Runter"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x02]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Pfeil Links"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x03]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Pfeil Rechts"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x04]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "OK / Enter"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x00]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Zurück"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x0D]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Home"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x10]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Info"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x35]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  # ==========================================================================
  # WIEDERGABE
  # ==========================================================================
  - platform: template
    name: "Play"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x44]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Pause"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x46]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Stop"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x45]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Vorspulen"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x49]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Zurückspulen"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x48]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Nächster Titel"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x4B]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Vorheriger Titel"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x4C]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  # ==========================================================================
  # ZAHLENTASTEN
  # ==========================================================================
  - platform: template
    name: "Taste 0"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x20]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 1"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x21]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 2"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x22]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 3"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x23]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 4"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x24]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 5"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x25]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 6"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x26]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 7"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x27]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 8"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x28]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

  - platform: template
    name: "Taste 9"
    on_press:
      - hdmi_cec.send:
          destination: 0x0
          data: [0x44, 0x29]
      - hdmi_cec.send:
          destination: 0x0
          data: [0x45]

1 „Gefällt mir“

Wichtig! HDMI Port „physicac_address“ an dem das Teil steckt richtig definieren!
Ich musste meine Adresse auf „address: 0x04“ ändern da ich unter „0x05“ schon ein Audio-System dran habe!

Hex Typ
0x00 TV
0x01 Recording Device
0x02 Reserved
0x03 Tuner
0x04 Playback Device
0x05 Audio System
1 „Gefällt mir“

Super und vielen Dank! Ich habe den Stick jetzt espHome fähig gemacht, er wird jedenfalls als online angezeigt. Für heute mache ich aber Pause, hatte jetzt ein paar Nachtschichten und jetzt ist erstmal Zeit mit meiner Frau dran. :grinning_face:

1 „Gefällt mir“

Meine Lösung seit über 15 Jahren sieht so aus. (Infrarot Modul)

Dashboard auf meinem Touch Kassenmonitor mit kompletter Steuerung von TV und Video.

1 „Gefällt mir“