RF433-Fernbedienungen in Home Assistant einbinden (MQTT + Lern-UI)

Ich habe ein DIY-Projekt veröffentlicht, das günstige 433-MHz-RF-Fernbedienungen und Funkschalter in flexible Controller für Home Assistant verwandelt.
Die Hauptmotivation war, dass Haushaltsmitglieder weiterhin die Fernbedienungen nutzen können, die sie bereits gewohnt sind, um damit Home-Assistant-Geräte (z. B. Smart-Plugs) zu steuern.

Das System hört auf RF433-Signale, lernt Codes live und ordnet sie über MQTT und eine benutzerdefinierte Lovelace-Karte Aktionen in Home Assistant zu.
Sobald der Empfänger läuft, sind keine fest kodierten RF-Mappings mehr erforderlich.

Highlights:

  • Live-RF-Lernmodus mit Protokollerkennung
  • Webbasierter Editor zur Zuordnung von RF-Codes zu HA-Entitäten und Services
  • MQTT-basierter Backend-Ansatz
  • Unterstützung mehrerer RF-Protokolle (ASK/OOK-basiert) und variabler Code-Längen
  • Ausgelegt für Arduino- / ESP-basierte 433-MHz-Empfänger
  • Getestet mit ESP32 und CC1101-Transceiver

:backhand_index_pointing_right: GitHub-Repository:

Feedback, Ideen und Beiträge sind willkommen.

2 „Gefällt mir“

Perfekt, ein RF 433-Empfänger als DIY-Lösung :+1: gleich mal gebookmarked und auf die Projekte-Liste gesetzt!

Neu: Der RemoteButton-to-HA Action editor kann jetzt auch ZigBee Remote Scene Buttons komfortable den gewünschten HA Aktionen zuordnen.

@Dschuli
Hi,
habe mir jetzt mit einem ESP32 Wroom das ganze mal aufgebaut.
Die Verdrahtung scheint zu passen aber es wird im Log nichts empfangen.
Ist eine Fernbedienung “CasaFan” 433,9Mhz Sub-class20
Schalten mit der Fernbedienung geht.
ESp32 (normal) und Funkmodul beides auch schon mal getauscht.
Denke es liegt an dem Funkstandard von CasaFan. Hab mal gegoogelt. Die kochen wohl ihre eigene Suppe.
Noch eine Idee ?

Mit dem eigenen Süppchen hast du wahrscheinlich recht. Ist wahrscheinlich ein eigenes OOK/ASK basiertes Protokoll. Suche mal in ChatGPT (oder ähnlich) auf “Casafan remote control protocol” und “Fernbedienung “CasaFan” 433,9Mhz Sub-class20”. Du kannst auch den RAW dump einschalten und schauen ob bzw. welches Signal du von der Fernbedienung bekommst.

In **esphome\433mhz-sniffer.yaml**musst du dazu die die Kommentierung der dump-Zeile (# weg) entfernen

# dump: ${rf_dump_mode} # Logging: rc_switch (debug), all (verbose) - if you dont want dumping comment that line

und in esphome\hardware-config.yaml den rf_dump_mode auf all setzen.

> rf_dump_mode: rc_switch # Logging mode (dump): 'rc_switch' (recommended), 'all' (verbose)

Nach Neukompilierung, z.B. mit esphome run “433mhz-sniffer.yaml”, solltest du im log sehen, was die Fernbedienung sendet. Eventuell kann die KI/ChatGPT helfen, das als ein protocol / code Format zu interpretieren.

@Dschuli
Ok.
Das steht bei Betätigung einer Taste jetzt drin.
Also scheint die Schaltung generell zu gehen.

Hier noch ein Ausschnitt wenn ich 3 verschiedene Tasten drücke.

[05:48:49.595][D][remote.beo4:086]: Beo4: n_sym=192
[05:48:49.601][I][remote.pronto:232]: Received Pronto: data=
[05:48:49.601][I][remote.pronto:240]: 0000 006D 0060 0000 000E 00CA 000B 0016 000C 0014 001E 0014 000E 0013 001F 0013 000E 0013 001F 0013 001F 0013 000E 0012 0020 0012 000F 0012 000F 0012 000F 0012 001F 0012 0010 0011 0011 0011 0020 0012 0010 0012 0010 0011 0010 0011 
[05:48:49.603][I][remote.pronto:240]: 0010 0012 000F 0012 000F 0012 000F 0012 0010 0011 0020 0012 0010 0013 000D 0012 0011 0073 0042 0011 0020 0011 0021 0011 0010 0012 0010 0013 000D 0012 0020 0011 0010 0011 0020 0012 000F 0011 0020 0012 0020 0012 000F 0011 0020 0012 
[05:48:49.609][I][remote.pronto:240]: 0010 0011 0011 0011 0010 0012 0020 0011 000F 0012 0010 0011 0020 0012 000F 0012 000F 0012 000F 0012 0010 0011 0010 0011 0011 0011 000F 0013 000F 0011 0020 0012 0010 0012 0010 0011 0010 0074 0041 0012 0020 0012 0020 0011 0010 0011 
[05:48:49.633][I][remote.pronto:240]: 0010 0012 000F 0012 0020 0011 0010 0011 0020 0012 0010 0011 0020 0013 001F 0011 000F 0012 0021 0011 000F 0012 0010 0012 000F 0012 001F 0012 000F 0012 000F 0011 0021 0012 0011 000F 0010 0011 0010 0011 000F 0012 000F 0012 0010 0011 
[05:48:49.654][I][remote.pronto:240]: 0010 0011 000F 0012 0020 0012 000F 0012 0010 0011 000E 0134 
[05:49:05.234][D][remote.beo4:086]: Beo4: n_sym=132
[05:49:05.251][I][remote.pronto:232]: Received Pronto: data=
[05:49:05.251][I][remote.pronto:240]: 0000 006D 0042 0000 000E 00A9 000D 0014 000C 0015 000D 0014 001E 0013 000E 0013 0020 0013 000E 0012 0020 0011 001F 0013 000E 0013 0020 0012 0010 0012 000F 0012 000F 0012 0020 0012 000F 0012 000F 0012 0020 0012 0010 0012 000F 0012 
[05:49:05.252][I][remote.pronto:240]: 000F 0012 000F 0012 001F 0012 000F 0012 000F 0012 000F 0012 0021 0011 0011 0011 0010 0011 0010 0073 0042 0011 0021 0011 0021 0011 0010 0011 0011 0010 000F 0012 0020 0012 0010 0011 0020 0011 000F 0012 0020 0011 0020 0012 0010 0012 
[05:49:05.265][I][remote.pronto:240]: 0021 0010 0012 000F 0011 0011 0010 0011 0021 0011 0010 0011 0010 0011 0020 0012 0010 0011 0010 0011 000F 0012 000F 0012 001F 0012 0010 0011 000F 0012 000F 0012 0020 0011 0010 0011 0010 0011 0010 0074 0041 0012 0020 0134 
[05:49:15.650][I][remote.pronto:232]: Received Pronto: data=
[05:49:15.654][I][remote.pronto:240]: 0000 006D 0001 0000 000E 0134 
[05:49:15.756][D][remote.beo4:086]: Beo4: n_sym=177
[05:49:15.765][I][remote.pronto:232]: Received Pronto: data=
[05:49:15.766][I][remote.pronto:240]: 0000 006D 0059 0000 0021 001D 0015 001E 0014 000E 0013 001F 0014 000D 0013 000F 0012 000F 0012 001F 0013 000F 0011 000F 0013 001F 0012 000F 0012 000F 0012 000F 0012 000F 0012 0010 0011 0010 0011 000F 0012 0020 0012 0020 0012 000F 
[05:49:15.766][I][remote.pronto:240]: 0011 000F 0012 0010 0073 0042 0011 0021 0011 0021 0011 0010 0011 0012 0010 0010 0011 0021 0011 0010 0011 0020 0012 000F 0011 0021 0011 0021 0011 0010 0011 0020 0012 0010 0011 0011 0010 0011 0011 0021 0011 0010 0011 0011 0011 0020 
[05:49:15.771][I][remote.pronto:240]: 0012 0010 0011 0010 0012 0010 0011 0010 0012 0010 0012 000F 0011 0010 0011 0021 0011 0020 0012 000F 0012 000F 0012 000F 0074 0041 0011 0021 0012 0020 0011 0010 0011 0010 0011 0010 0012 0020 0011 0011 0010 0021 0012 000F 0012 0021 
[05:49:15.793][I][remote.pronto:240]: 0011 0021 0011 0010 0011 0021 0011 0010 0012 0010 0011 0010 0011 0020 0011 0011 0011 0010 0011 0022 0010 0010 0011 0010 0011 0010 0012 0010 0011 000F 0012 0010 0011 0010 0011 0021 0011 0020 0011 0010 0011 0007 0134 

Nach was müsste ich jetzt suchen ?
Müsste da jetzt auch in der Dashboard Card was stehen?
Grüße Markus

Das sagt ChatGPT dazu wenn ich die Codefolge analsysieren lasse.

Entscheidender Teil: Die Pulse

Du hast hauptsächlich:

000E ≈ 140010 ≈ 16001F ≈ 310020 ≈ 320021 ≈ 3300A9 ≈ 1690073 ≈ 115

Das sind relative Zeiten.

Die Daten zeigen sehr klar:

  • kurze Pulse
  • lange Pulse
  • Synchronlücken
  • wiederholte Frames

Das sieht stark nach:

  • OOK/ASK
  • Manchester-/PWM-artiger Codierung
  • Fixed Code

aus.

Nicht nach Rolling Code.


Wahrscheinlich dekodierte Struktur

Sehr wahrscheinlich:

Timing Bedeutung
~14–16 kurzer Puls
~31–33 langer Puls
~115 Sync
~169 Startburst

Das ist typisch für:

  • EV1527-artige Protokolle
  • Lüftersteuerungen
  • einfache RF-Encoder

CasaFan nutzt oft solche Varianten.

Kannst Du damit was anfangen ?

Ist definitiv kein rc-switch code, der primär von meinem sniffer supported wird (sonst käme da rc_switch). Damit ist klar, dass der sniffer kein event generiert.

Pronto und Beo sind wahrscheinlich fälschlich erkannte Protokolle, wobei pronto ein älteres Protokoll ist, das sehr an die raw Signale angelehnt ist. Setze in der 433mhz-sniffer.yaml den dump auf raw (dump: raw) und schicke mir die raw dumps - jeweils ein paarmal ( 5 mal) für jede Taste deiner Fernbedienung (am besten jeweils ein file pro Taste). Ich schau mal. ob ich mit KI Unterstützung da was verwendbares rauskitzeln kann.

logs_433mhz-sniffer_logs.txt (24,8 KB)
Das ist mal die Taste “0”.
Wenn der Log so gemeint war mache ich es auch mit den anderen Tasten.
Irgendwie schaut die Codierung bei jedem drücken anders aus ?
Der Log rennt nämlich immer weiter .-)

Nur als Hinweis:

In der hardware-config.yaml steht unter status_led_pin: bei Dir jetzt GPIO4 drin.
In der wiring Beschreibung GPIO12. Habs dann geändert auf…
status_led_pin: GPIO12
Sonst brennts Lamperl net. .-)

GPIO Pin für die LED ist ein copy mistake - hab ich bei meinem ESP - ist aber lt. docs ist GPIO12 besser/safer. Danke fürs Bemerken.

Re logs:
Dies kurzen bursts snd sicher noise - heissen also nix. Man sieht aber 5 Wiederholungen von 4/5 langen bursts- die sind offenbar die Fernbedienung. Wirklich erkennen kann ich da auch nicht wirklich was brauchbares - die KI aber schon schon. Meint

The good frames are here. Those are clearly:

* structured
* repeated
* binary encoded
* replayable

This is absolutely decodable.

what would the repeated payload be? should be sent 5 times (+repetitions)

I’ll treat the clean long `remote.raw:027` blocks as real frames and ignore the short fragmented `remote.raw:036` noise. Then I’ll normalize the ~420/850 µs pairs into bits.

The repeated payload looks like this 32-bit frame:

11000101011010001001000000101000


Hex:

C5689028


Structure I see:

leader ≈ +1700 µs then 32 bits: short gap ≈ -420 µs short pulse ≈ +420 µs = 0 short gap ≈ -420 µs long pulse ≈ +850 µs = 1 frame gap ≈ -3000 µs repeat


Example repeated clean frame starts here and repeats after the ~`-3000` gap:

Wenn du das weiter verfolgen willst: Setze in der hardware-config.yaml

# Enable/disable raw pulse logging in C++ handler. Set to 1 to enable, 0 to disable
enable_raw_logging: 1

und schicke mir die 5 x drücken logs für ein paar Tasten - jeweils als separate files. Mal schauen ob ich damit was anfangen kann.

Danke erst mal für die Mühe. Vielleicht habe ich ja Glück.
logs_433mhz-sniffer_logs_Taste_Licht.txt (29,2 KB)
logs_433mhz-sniffer_logs_Taste_3.txt (38,3 KB)
logs_433mhz-sniffer_logs_Taste_2.txt (26,0 KB)
logs_433mhz-sniffer_logs_Taste _1.txt (29,8 KB)
logs_433mhz-sniffer_logs_Taste_0.txt (29,6 KB)

Okay - check the branch GitHub - Dschuli/rf433-remote-ha-mapper at feature/casa_fan · GitHub
Du braucht nur die esphome\433mhz-sniffer.yaml und esphome\rf_handlers.h.

Sollte kompilieren. Ohne die Fernbedienungen ist weiteres testen/feintunen für mich nur schwer möglich. Also hoffen wird, dass das auf Anhieb funktioniert. Sonst würd ich an deiner Stelle mal eine KI mit dem Code füttern und einfach mitteilen ,was nicht geht. Mal schauen was sie dann sagt (oder halluziniert)…

So zwei logs.
Er zeigt jetzt im Standard log jetzt was an.
Wenn Du möchtest schreibe mir per PN, dann könnte ich Dir die Fernbedienung schicken.
Wenn das für Dich einfacher ist.
Im Dashboard zeigt er aber noch nichts an, denke weil das auch angepasst werden muß .-)

logs_433mhz-sniffer_logs_Taste_Licht.txt (6,1 KB)
logs_433mhz-sniffer_logs_Taste_3.txt (6,1 KB)
logs_433mhz-sniffer_logs_Taste_2.txt (6,1 KB)
logs_433mhz-sniffer_logs_Taste_1.txt (6,1 KB)
logs_433mhz-sniffer_logs_Taste_0.txt (6,1 KB)

An sich schauen diese Logs sehr gut aus. Bitte checke, ob im HA ein event ankommt. Unter /developer-tools/event listen to ‘esphome.rf433’. Bei Tastendruck müsste da ein event angezeigt werden. Wenn ja - checke ob im allgemeinen HA log irgendein Fehler auftaucht, der mit der RF433 Automation zu tun hat. Weiters ob nach dem Drücken im Helper RF433MHz Last Event Store ein entsprechender event aufscheint.

Also es arbeitet, aber evtl klemmt noch was oder Userfehler meinerseits.
Was habe ich gemacht:
Alle Debug Parameter wieder auf Standard gesetzt.
Status Led erst dauerhaft grün.
Learning mode ist auf aus.
Im Dashboard kommt nach einem Tastendruck das Event an.
Dann geht die Status Led sofort auf rot.
Kann alle Tasten durchschalten, der Code ist dann jeweils ein anderer.
Led bleibt rot.
Aktivitätsprotokoll Homeassistant rennt ab diesem Zeitpunkt durch und stoppt nicht mehr.
Schalte den Learning mode ein.
Mache erstmal nichts anderes als auf Cancel zu gehen.
Bricht aber den Vorgang nicht ab.
Die 433mhz-sniffer log funktioniert aber weiter.
Das Aktivitätsprotokoll von Homeassistant rennt weiter durch.

Auch mit Cancel nicht zu stoppen.
Erst nach an und ausstecken des ESP32 kann ich die Schleife unterbrechen und danach mit Cancel den Learning mode wieder auf off stellen..
Verhalten beliebig reproduzierbar.

Wie ich diesen Code jetzt anwenden kann um damit jetzt ein Signal ohne Fernbedienung über den Sniffer an den Lüfter zu schicken habe ich noch nicht verstanden, muß erst warten was mit der Dauerschleife rauskommt. .-)

Mache ich was falsch oder Bug ?

Habe den code leicht verändert (und frisch gepushed) - an sich nur rf_handler.h. Sieht so aus, dass nach einem gültigen burst der gültig marker (debounced) bei neuerlichem Aufruf nicht zurückgesetzt wurde - und damit bei jedem noise ein neues event mit alten Daten ausgelöst wurde.

Mein code sieht allerdings kein raussenden von codes vor - es geht nur darum auf FB Tastendruck irgeneine HA Aktion auszulösen (die über die learning card flexibel und komfortable definiert werden kann). Es geht also um die Weiterverwendung gewohnter, alter Fernbedienungen in HA - mein Sniffer hat dzt. keine Sendefunktionalität.

ESPHome hat kürzlich RF Funktionen bekommen/erweitert. Ev. ist da was dabei das weiterhilft. Ich habe ab er keine Erfahrung damit. Den erkannten code zu replayen müsste möglich sein, aber mehr kann ich dazu auch nicht beitragen.

Nachtrag:
Habs gecheckt, in ESPHome gibt es

remote_transmitter.transmit_raw Action

This action sends a raw code to a remote transmitter.

on_...:  - remote_transmitter.transmit_raw:      code: [4088, -1542, 1019, -510, 513, -1019, 510, -509, 511, -510, 1020,             -1020, 1022, -1019, 510, -509, 511, -510, 511, -509, 511, -510,             1020, -1019, 510, -511, 1020, -510, 512, -508, 510, -1020, 1022,             -1021, 1019, -1019, 511, -510, 510, -510, 1022, -1020, 1019,             -1020, 511, -511, 1018, -1022, 1020, -1019, 1021, -1019, 1020,             -511, 510, -1019, 1023, -1019, 1019, -510, 512, -508, 510, -511,             512, -1019, 510, -509]

Configuration variables

  • code (Required, list, templatable): The raw code to send as a list of integers. Positive numbers represent a digital high signal and negative numbers a digital low signal. The number itself encodes how long the signal should last (in microseconds).
  • carrier_frequency (Optional, float, templatable): Optionally set a frequency to send the signal with for infrared signals. Defaults to 0Hz.
  • All other options from Remote Transmitter Actions.

CasaFan Protokoll wird nicht direkt unterstützt. Aber - wie gesagt - der sniffer hat dzt keine Sendefunktion implementiert.

:crayon:by HarryP: Zusammenführung Doppelpost (bei Änderungen oder hinzufügen von Inhalten bitte die „Bearbeitungsfunktion“ anstatt „Antworten“ zu nutzen)

OK.
Dann werde ich mal versuchen ob ich da mit dem remote_transmitter.transmit_raw mir was erlesen kann. Gehe ich richtig davon aus das ganze dann in die ESP Home Programierung schon mit rein muß?
Das mit dem Dauer loop nach drücken der Taste und Led auf rot hast Du ne Idee?

Möchtest Du das mal probieren ? Es kommt mit ChatGPT ein gutes Ergebnis raus das Du vielleicht, da schon eingearbeitet hast, leichter verstehst als ich.
Ich habe die als letzt von Dir erstellten Dateien:
433mhz-sniffer.yaml, hardware-congig.yaml und rf_handlers.h in eine ZIP Datei und bei Chat GPT hochgeladen. Dazu diese Frage gestellt.

“Momentaner Code kann nur RF 433mhz Signale empfangen und im Debug auch als RAW Daten ausgeben. Es gibt einen neuen Befehl “remote_transmitter.transmit_raw” der es erlaubt auch wieder diese Daten zu senden. Kann man diese Funktion Implementieren ?”

Servus
Ich habe genau das gerade hinter mir:

Aufgabe: Original Fernbedienung in HA/ESPHome einlernen um die Befehle dann später per HA Button wieder zu senden.
Wie beschrieben war es kein Honiglecken und gespickt mit Fallstricken. Ohne ständige Kontrolle der Protokolls per Oszilloscope hätte ich es nie geschafft.
Hier mein ESPHome code als Referenz: Die gesendeten Daten und das Timing wird bei dir natürlich nicht passen, aber evtl. hilft es dir als Ausgangsbasis

esphome:
  name: esp-dachfenster
  friendly_name: ESP_Dachfenster

esp32:
  board: esp32dev
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "123"

ota:
  - platform: esphome
    password: "123"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: 10.0.0.157
    gateway: 10.0.0.138
    subnet: 255.255.255.0
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Esp-Dachfenster Fallback Hotspot"
    password: "123"

captive_portal:

script:
  - id: sende_raw_paket
    mode: parallel
    parameters:
      bits: string # Das Skript erwartet jetzt nur noch den 41-Bit Text!
    then:
      - remote_transmitter.transmit_raw:
          carrier_frequency: 38kHz
          code: !lambda |-
            std::vector<long int> raw_signal;
            for (int i = 0; i < 10; i++) {
                raw_signal.push_back(1650); raw_signal.push_back(-350); 
                size_t bit_index = 0;
                for (char b : bits) {
                    bit_index++;
                    bool is_last_bit = (bit_index == bits.length());
                    if (b == '1') {
                        raw_signal.push_back(705); raw_signal.push_back(is_last_bit ? -705 : -305);
                    } else if (b == '0') {
                        raw_signal.push_back(305); raw_signal.push_back(-705);
                    }
                }
                raw_signal.push_back(5000); raw_signal.push_back(-2500);
            }
            return raw_signal;


spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19

cc1101:
  id: my_cc1101
  cs_pin: GPIO05
  frequency: 433.92MHz

remote_receiver:
  pin: GPIO02
  tolerance: 50%
  filter: 250us
  idle: 60ms
  buffer_size: 10kb    # Drastisch erhöht von der Standardgröße (erlaubt riesige Telegramme)
  dump:
    - rc_switch

remote_transmitter:
  pin: GPIO04
  carrier_duty_percent: 100%
  on_transmit:
    then:
      - cc1101.begin_tx
  on_complete:
    then:
      - cc1101.begin_rx

button:
  - platform: template
    name: "Dachfenster Rechts Auf"
    icon: "mdi:window-maximize"
    on_press:
      - script.execute:
          id: sende_raw_paket
          bits: "10010111111010101000100010000000111101011"

  - platform: template
    name: "Dachfenster Rechts Stop"
    icon: "mdi:window-maximize"
    on_press:
      - script.execute:
          id: sende_raw_paket
          bits: "10010111111010101000101010000000111101101"
  
  - platform: template
    name: "Dachfenster Rechts Zu"
    icon: "mdi:window-maximize"
    on_press:
      - script.execute:
          id: sende_raw_paket
          bits: "10010111111010101000001110000000111111111"

Ui. Toll.
Jetzt bin ich gerade etwas raus mit dem Verständnis der GPIO´s
Die Pin´s GPIO04 und GPIO02 ist das interne Programmierung oder ESP32 zu cc1101 ?
Das hatte ich bei der KI Suche heute auch schon das ich nicht wusste wie das dann gemeint war.

Nein, die sind mit GDO0 bzw. GDO2 verbunden. Die Daten welche gesendet oder empfangen werden gehen darüber.
Befehle und Steuerkommandos bekommt der CC1101 über das SPi Interface.

gruß
bb