Sonoff RF Bridge. Strategies for receiving data

Is this only good for rf sensors rather than switches ? Got a number of switches I want to setup.

Thanks man, worked brilliantly!

Hello,

First, thanks @123 for the great idea. It opens the usage of Python scripts for me. I’m working on another step: sending data. Because I don’t see any such topic, I’m asking here (it’s similar).

I have some shutters like this:

cover:
  - platform: mqtt
    name: "Volet bureau"
    command_topic: "cmnd/rfbridge/backlog"
    payload_close: "RfRaw AA B0 38 03 08 03F2 07B2 17B6 XXXX 55; Delay 20; RfRaw 0"
    payload_open: "RfRaw AA B0 38 03 08 03F2 07B2 17B6 XXXX 55; Delay 20; RfRaw 0"
    payload_stop: "RfRaw AA B0 38 03 08 03E8 07A8 17B6 XXXX 55; Delay 20; RfRaw 0"

It makes my configuration.yaml messy. The idea is to change it to this:

cover:
  - platform: mqtt
    name: "Volet bureau"
    command_topic: "remux/volet_bureau/set"

It’s easy, with this script:

rfraw = {
  'volet_bureau': {
    'open': 'AA B0 38 03 08 03F2 079E 1716 XXXX 55',
    'close': 'AA B0 38 03 08 0406 07C6 1720 XXXX 55',
    'stop': 'AA B0 38 03 08 03DE 07A8 1798 XXXX 55',
  }
}

action = data.get('payload')
device = 'volet_bureau' # how to take it from the MQTT topic?
if action is not None:
  action = action.lower()
  if action in rfraw[device].keys():
    service_data = {
      'topic': 'cmnd/rfbridge/backlog',
      'payload': 'RfRaw {}; Delay 20; RfRaw 0'.format(rfraw[device][action]), 'qos':0, 'retain':'false'
    }
    hass.services.call('mqtt', 'publish', service_data, False)

There will be some more thing to set the state after rfraw too, but that’s something I’d take care of later. Now the problem here is I’d like to listen to multiple topics (e.g. remux/#/set) and use one script/automation for multiple shutters. Is there anyway to know the actual topic (device variable in the above script) on which the message was posted?

Yes, that information will be in the Trigger State Object and accessible as trigger.topic.

From the documentation:

MQTT

Template variable Data
trigger.platform Hardcoded: mqtt .
trigger.topic Topic that received payload.
trigger.payload Payload.
trigger.payload_json Dictonary of the JSON parsed payload.
trigger.qos QOS of payload.

Use it to extract the text you want (from the topic) then pass it to the python_script.

1 Like

Great, thank you! I didn’t notice what I can pass arbitrary data to the Python script service, not just “payload” key.

Problem solved with device = data.get('topic').split('/')[1].

How could you do it in parallel mode? I try all modes and nothing works reliably. It seems that Python scripts can not be parallelismed. I got these warning in any mode:

2021-01-20 08:47:00 WARNING (MainThread) [homeassistant.components.automation.rf_bridge_demux] RF Bridge remux: Already running
2021-01-20 08:47:09 WARNING (MainThread) [homeassistant.components.automation.rf_bridge_demux] RF Bridge remux: Already running
2021-01-20 08:47:09 WARNING (MainThread) [homeassistant.components.automation.rf_bridge_demux] RF Bridge remux: Already running

I use automation to send command to all devices at the same time. I have 4 shutters. When my script calls the MQTT service once, 3 or sometimes 4 all works. But then I’m adding more MQTT publish call in the script, and now only 1 works.

My script, which subscribes to remux/#:

rfraw = {
  # ... a couple dozens of lines
}

action = data.get('payload')
_, device, command = data.get('topic').split('/')
if command == 'set' and action is not None:
  action = action.lower()
  logger.warning(device + ' ' + action)
  if action in rfraw[device].keys():
    service_data = {
      'topic': 'cmnd/rfbridge/backlog',
      'payload': 'RfRaw {}; Delay 20; RfRaw 0'.format(rfraw[device][action]),
      'qos': '0',
      'retain': 'false'
    }
    hass.services.call('mqtt', 'publish', service_data)
    logger.warning(device + ' ' + action + ' rf')
    service_data = {
      'topic': 'remux/' + device + '/state',
      'payload': 'open' if action == 'open' else 'closed',
      'qos': '1',
      'retain': 'true'
    }
    hass.services.call('mqtt', 'publish', service_data)
    logger.warning(device + ' ' + action + ' state')
    service_data = {
      'topic': 'remux/' + device + '/position',
      'payload': '100' if action == 'open' else '0' if action == 'close' else '50',
      'qos': '1',
      'retain': 'true'
    }
    hass.services.call('mqtt', 'publish', service_data)
    logger.warning(device + ' ' + action + ' position')

Then I use cover.cover_open service in an automation to open 4 covers by sending “open” to remux/DEVICE/set 4 times.
A workaround would be calling cover.cover_open service 4 times for 4 devices with a waiting time between each, but it’s not ideal.

Edit: I’ve found Automation - call scripts with multiple actions for entity list - sequentially? which consists of writing a helper service which calls the main service in sequence with delay.

Whilst I realise this is quite an old thread, I thought I would leave my own take on the strategy 2 python_script approach for demux’ing the signals.

It is basically the same as main script with a little extra hack added to support automatic home assistant MQTT discovery, so it is a little easier to manage as you don’t need to worry about manually creating (or recreating) the sensors. Just add them to the script, trigger them once and away you go :slight_smile:.

I have used this in a few setups for friends (so comments in the code :wink:) with several bridges who had a need to use a load of existing 433 sensors.

# Script to demux the RF codes received on MQTT topics from the bridge and send them back to new topics and automatically setup them as sensors in HA.

# Tested with Sonoff RF bridges with Tasmota but the logic will work with any RF bridge that exposes to MQTT.
# You can get your codes by looking at the Console log on any of your RF Bridges as devices are triggered.

# If you want to support multiple RF bridges you can either have them all post to the same MQTT topic (a tiny bit more efficient BUT you can't tell the bridge messages apart)
# or just set this script up to trigger on each topic individually.

# Only support for simple Binary Sensors right now.
# TODO: Expand to add discovery/setup for switches and the right command_topic for the bridge.

# Adapted from the scripts kindly posted to: https://community.home-assistant.io/t/sonoff-rf-bridge-strategies-for-receiving-data/108181

# Name that is used in the discovery node_id along with the construction of 'fake' model and manufacturer to make filtering discovered devices nice and easy.
FAKE_NAME = 'rfbridge'
# Topic Root - Just the root topic (with /) where you want your states published.
TOPIC_ROOT = 'internal/ha-rf433/'

# device_class MUST be valid for a Home Assistant Binary Sensor (see https://www.home-assistant.io/integrations/binary_sensor/)
# 
# Try the following: 
# 
# 'light', 'power', 'plug', 'door', 'window', 'garage_door', 'lock'

# RF Codes and Setup Information:
#
# You need to setup the RF codes for your environment, names and some vanity settings in this part of the script.
# It is used to check for supported codes and also used to populate the MQTT discovery so that the devices pop up in 
# Home Assistant the 1st time this script 'sees' the sensor.
#
# Format:
#
# RF Code (as seen by your bridge) : topic subname (also used in for discovery, use the same for both states), 
# state (ON or OFF, repeat the complete line for both if your device has discreate commands for both), 
# retain (true for saved state, false for unknown or single state), device_class

entities = {
    'D3503B':['rf_dining_4_gang_extension_3','ON','true','power'],
    'D3503A':['rf_dining_4_gang_extension_3','OFF','true','power'],
    'A39C1B':['rf_dining_4_gang_extension_3','ON','true','power'],
    'A39C1A':['rf_dining_4_gang_extension_3','OFF','true','power'],
    'D3503F':['rf_dining_4_gang_extension_1','ON','true','power'],
    'D3503E':['rf_dining_4_gang_extension_1','OFF','true','power'],
    'D35037':['rf_dining_4_gang_extension_2','ON','true','power'],
    'D35036':['rf_dining_4_gang_extension_2','OFF','true','power'],
    'D35033':['rf_dining_4_gang_extension_4','ON','true','power'],
    'D35032':['rf_dining_4_gang_extension_4','OFF','true','power'],            
    '33858F':['rf_lounge_strip','ON','true','light'],
    '33858E':['rf_lounge_strip','OFF','true','light'],
    '93620A':['door_sensor_1','ON','true','door'],
    '9B620E':['door_sensor_1','OFF','true','door'],
}

# With some luck you should not need to edit below this point.

payload = data.get('payload')

if payload in entities:
    try:
        topic, state, retain, device_class = entities[payload]
    except ValueError:
        topic = '{0}unknown'.format(TOPIC_ROOT),
        state = payload
        retain = 'false' 
        device_class = 'unknown'
        logger.warning(
            '<rfbridge_demux> Received invalid RF command: {0}'.format(payload)
        )
    
    service_data = {
        'topic': '{1}{0}'.format(topic, TOPIC_ROOT),
        'payload': '{0}'.format(state),
        'qos': 0, 
        'retain': '{0}'.format(retain),     
    }

    discovery_data = {
        'topic': 'homeassistant/binary_sensor/{1}/{0}/config'.format(topic, FAKE_NAME),
        'payload': '{{"name": "{0}", "state_topic": "internal/ha-rf433/{0}", "device_class": "{1}", "unique_id": "{0}-{2}", "mdl": "{2}-{1}", "mf": "{2}" }}'.format(topic, device_class, FAKE_NAME),
        'retain': 'true'
    }

    hass.services.call('mqtt', 'publish', service_data, False)
    hass.services.call('mqtt', 'publish', discovery_data, False)

# Remove these last lines if you don't want to fill your log every time your bridge catches a code that you're not working with.
else:
    logger.warning(
        '<rfbridge_demux> Received unregistered RF command: {0}'.format(payload)
    )
1 Like

You only need to publish a discovery payload (as a retained message) to a discovery topic once. The example you posted publishes it every time a device’s state changes.

In addition, an RF Bridge is capable of receiving data from devices belonging to someone else (nearby). It (i.e. your python_script) will automatically create entities for the neighbor’s gadgets as well (i.e. unwanted entities may appear).

Automatically creating the entity seems convenient but the resulting entity is inert until its characteristics are defined within the python_script’s dictionary. Given that one still needs to do that manually, it’s only marginally more effort to define the entity itself.


FWIW, I define all my MQTT-based devices via MQTT Discovery using scripts or simply via Developer Tools > Services. For example, here’s one I created recently:

The following can serve as a template for creating other sensor entities:

topic: homeassistant/sensor/ec_forecast/config
retain: true
payload: >
  {
    "name": "Forecast",
    "state_topic": "home/weatheroffice/forecast",
    "unique_id": "ec_weather_forecast_today",
    "device": {
        "identifiers": ["ec_weather"],
        "name": "Environment Canada Weather",
        "model": "Toronto",
        "manufacturer": "Environment Canada",
        "sw_version": "1.X"
    }
  }

The device section is optional but handy for combining related entities into a common device.

1 Like

@123

Actually, it’s a very fair point on the retain and recreation of the discovery payload, as mentioned, this was a quick hack I did some time ago to help friends out and not something I gave that much attention to. I should clean that up. Looking at it again, I have also noticed a few other areas that need cleaning up and I should make discovery optional.

That said, even in its current state, it will only discover items that are defined as entities being monitored in the dictionary at the top (and that was expanded with some extra hints for the device_class etc.), it won’t create any discovery items for unknown entities, that was a conscious choice to prevent what you describe happening. Unknown codes are logged to HA however (if you have warning or higher setup on the component) unless you remove the last few lines.

The idea behind this was that it could be used for a very basic setup, you configure the entities you want in it and it did not require any further input.

Personally, I agree that for anything more structured/complex than a basic single sensor or single switch in isolation, creating them inside HA is the way to go :slight_smile:.

Thank you for this. It has helped a lot with my 11 door sensors and 3 water leak sensors. A few things I have noticed that may be obvious to some, but wasn’t obvious to me:

  1. I initially only wanted this for a few door sensors, and just added those to the dictionary. If you don’t add them, you will get a warning in the logs that dupex doesn’t know the code. Yep, makes sense. I added all the sensors. However

2). What do you do for the battery code? I will add my code on the bottom. I have a guess, and I will probably try it, but just curious.

3). What is the best recommendation for water leak sensors that just have one code? I have it on false.

Here is my code for a door sensor. Currently, I still have the old code for the battery sensor.

  # Door Sensor 2
  - platform: mqtt
    name: "Back Door"
    state_topic: "home/sensor2"
#    value_template: '{{value_json.RfReceived.Data}}'
#    payload_on: "08C20A"
#    payload_off: "08C20E"
    device_class: door
    qos: 1
  - platform: mqtt
    name: "Back Door sensor battery"
    state_topic: "tele/RFBridge/RESULT"
    value_template: '{{value_json.RfReceived.Data}}'
    payload_on: "08C206"
    device_class: battery
    qos: 1

and my script in python .

      '08C20A':['sensor2','ON','true'],
      '08C20E':['sensor2','OFF','true'],

So after reading all 353 posts, is this correct for my yaml and my python script?

  - platform: mqtt
    name: "Back Door sensor battery"
    state_topic: "home/sensor2_battery"
#    value_template: '{{value_json.RfReceived.Data}}'
#    payload_on: "08C206"
    device_class: battery
    qos: 1
      '08C20A':['sensor2','ON','true'],
      '08C20E':['sensor2','OFF','true'],
      '08C206':['sensor2_battery','ON','true']

@123 thanks for your work on this. I am new to all of this and after spending hours scanning various threads I am unable to figure out how to modify this for my needs. I’m using a RF Bridge with Portisch to control some none iTead devices through the RfRaw 177 command which returns a greater tele/RF_Bridge/RESULT payload. The payload is not always exactly the same using this method but it does contain a string for each device’s different commands (up, down, stop, etc.). Can you help me understand how to modify the dictionary to use a contain for the payload data? Is this possible? Thanks.

I don’t understand your question. What do you mean by “use a contain”?

@123 Sorry my confusing post. Here is an example below of the data payload received by the bridge for the same device’s on command:

"RfRaw":{"Data":"AA B1 05 0906 065E 0140 028A 15D6 C092B2A3B2A3B2B2A3A3B2B2A3A3A3A3A3A3B2A3A3A3B2A3B2A3B2A3A3A3A3A3B2B2B2B2B2A3B2B2A3B2 55"}}
"RfRaw":{"Data":"AA B1 05 0910 065E 014A 0280 15D6 C092B2A3B2A3B2B2A3A3B2B2A3A3A3A3A3A3B2A3A3A3B2A3B2A3B2A3A3A3A3A3B2B2B2B2B2A3B2B2A3B2 55"}}
"RfRaw":{"Data":"AA B1 05 0910 0654 014A 0280 15CC C092B2A3B2A3B2B2A3A3B2B2A3A3A3A3A3A3B2A3A3A3B2A3B2A3B2A3A3A3A3A3B2B2B2B2B2A3B2B2A3B2 55"}}

I am trying to target the following payload data as the dictionary entry in your script as the sensor’s on state since it is always the same for that device’s command:

C092B2A3B2A3B2B2A3A3B2B2A3A3A3A3A3A3B2A3A3A3B2A3B2A3B2A3A3A3A3A3B2B2B2B2B2A3B2B2A3B2

The variables between the payload data “AA B1 05” and the “C092B2…” always vary.

How would I go about integrating this into your script if I am able to?

Thanks!

If AA B1 05 indicates the device’s state is on then just use that as the value in the dictionary. Modify the code so that it slices the first eight characters of the payload and use that sub-string to search the dictionary. In other words, only use the unchanging portion of the payload to identify the device’s state.

Got it. Thanks for your help.

Hi!

You could also try this: https://youtu.be/w_CchtI-oK0?t=400

It’s a digiblurDIY video, it explains how to setup rules on the RF bridge itself to send mqtt messages to a specific topic and a payload triggered by RF messanges, so there wouldn’t be a need for a dictionary on HA. It’s very good explained in the video.

So the “logic” is on the bridge, not HA.

You could create either a sensor or switch template that listens on that topic. Or even “cover”, they are specific for what I think you are trying to model.

1 Like

Hi,

I have problem with my Sonoff motion sensor and RF bridge and this script and Home Assistant.
So if receive motion on the sensor the HA turn on one light. My problem is sometime not turning on the light.
In the sonoff bridge have a log which is receive the code but nothing happend only the second move turn the light on. Sometime need more move to turn on the light.

10:01:43.632 MQT: tele/sonoff_rf/RESULT = {"Time":"2021-08-04T10:01:43","RfReceived":{"Sync":11940,"Low":430,"High":1220,"Data":"F61C4E","RfKey":"None"}}
10:01:52.037 MQT: tele/sonoff_rf/RESULT = {"Time":"2021-08-04T10:01:52","RfReceived":{"Sync":12530,"Low":390,"High":1180,"Data":"F61C4E","RfKey":"None"}}

why do this and how to fix it? Very annoying and useless. :frowning:
The connection not poor so that is not problem.

You can start by stating which of the two strategies you are using. If it’s strategy 1, post the relevant configuration.

If you are using strategy 2, post the contents of your python_script’s dictionary. Point out which entry in the dictionary represents the RF codes used to control the light. If you have made any significant modifications to the python_script or the demultiplexer automation, post them as well.

I used strategy 2
The second code what is my motion sensor:

d = { '2D530A':['boltajto','ON','true'],
      'F61C4E':['pincemozgas','ON','false'],
      '30E10A':['konyhaablak','ON','true'],
      '30E10E':['konyhaablak','OFF','true'],
      '2E180A':['zuhanyablak','ON','true'],
      '2E180E':['zuhanyablak','OFF','true']
      
    }

p = str(data.get('payload'))

if p is not None:
  if p in d.keys():
    service_data = {'topic':'home/{}'.format(d[p][0]), 'payload':'{}'.format(d[p][1]), 'qos':0, 'retain':'{}'.format(d[p][2])}
  else:
    service_data = {'topic':'home/unknown', 'payload':'{}'.format(p), 'qos':0, 'retain':'false'}
    logger.warning('<rfbridge_demux> Received unknown RF command: {}'.format(p))
  hass.services.call('mqtt', 'publish', service_data, False)

here is my automation:

alias: rfbridge_demultiplexer RF1
trigger:
  - platform: mqtt
    topic: tele/sonoff_rf/RESULT
action:
  - service: python_script.rfbridge_demux
    data_template:
      payload: '{{trigger.payload_json.RfReceived.Data}}'
mode: single

Please post the configuration for the MQTT Binary Sensor representing the motion sensor.

Please post the automation that is responsible for monitoring the motion sensor and the light that you said “sometime not turning on the light”.