Broadlink binding for RMx, A1, SPx and MP. Any interest?

This version PLUS are supported to?

https://www.aliexpress.com/item/2017-New-Broadlink-RM-plus-WiFi-Universal-Smart-Remote-Control-RF-IR-433-315-Hmz-for/32808024597.html

regards

I’ve got two RM3s running with Cato’s Broadlink binding, and they both auto-detect and don’t go offline in PaperUI and work great.

I did have 2 RM2’s using it, and they wouldn’t auto-detect (so had to be setup manually). While they were sometimes showing offline in PaperUI, they still worked via openHAB.

Thanks @DSTM, but i do not use this binding anymore because of @Cato_Sognen is unreachable and some time in the future this binding will not work anymore.

I switched to the this broadlink-mqtt package, it works like a charm. Usability is also much better because it is possible to record remotes via mqtt. No more copying of files and such stuff.

1 Like

I don’t see why the binding will stop working, but if it does then there might be another option available by then (if not, there’s alternatives such as what you’re using).

And you say what you’re using works like a charm, but i replied when you’d posted having issues.

But whatever works for you. :slight_smile:

Some time in the future some interfaces will change and without access to the source code this is a dead end.

At first i was using a fork, this didn’t go well. As soon as i switched to the original everything worked. I also enjoy the ability to record commands without additional effort.

Hi

try this code

#!/usr/bin/env python

import paho.mqtt.client as paho  # pip install paho-mqtt
import broadlink  # pip install broadlink
import os
import sys
import time
import logging
import logging.config
import socket
import sched
HAVE_TLS = True
try:
    import ssl
except ImportError:
    HAVE_TLS = False
from threading import Thread
from test import TestDevice

# read initial config files
dirname = os.path.dirname(os.path.abspath(__file__)) + '/'
logging.config.fileConfig(dirname + 'logging.conf')
CONFIG = os.getenv('BROADLINKMQTTCONFIG', dirname + 'mqtt.conf')


class Config(object):
    def __init__(self, filename=CONFIG):
        self.config = {}
        self.config['ca_certs']     = None
        self.config['tls_version']  = None
        self.config['certfile']     = None
        self.config['keyfile']      = None
        self.config['tls_insecure'] = False
        self.config['tls']          = False
        execfile(filename, self.config)

        if HAVE_TLS == False:
            logging.error("TLS parameters set but no TLS available (SSL)")
            sys.exit(2)

        if self.config.get('ca_certs') is not None:
            self.config['tls'] = True

        if self.config.get('tls_version') is not None:
            if self.config.get('tls_version') == 'tlsv1':
                self.config['tls_version'] = ssl.PROTOCOL_TLSv1
            if self.config.get('tls_version') == 'tlsv1.2':
                # TLS v1.2 is available starting from python 2.7.9 and requires openssl version 1.0.1+.
                if sys.version_info >= (2,7,9):
                    self.config['tls_version'] = ssl.PROTOCOL_TLSv1_2
                else:
                    logging.error("TLS version 1.2 not available but 'tlsv1.2' is set.")
            	    sys.exit(2)
            if self.config.get('tls_version') == 'sslv3':
                self.config['tls_version'] = ssl.PROTOCOL_SSLv3

    def get(self, key, default='special empty value'):
        v = self.config.get(key, default)
        if v == 'special empty value':
            logging.error("Configuration parameter '%s' should be specified" % key)
            sys.exit(2)
        return v


try:
    cf = Config()
except Exception, e:
    print "Cannot load configuration from file %s: %s" % (CONFIG, str(e))
    sys.exit(2)

qos = cf.get('mqtt_qos', 0)
retain = cf.get('mqtt_retain', False)

topic_prefix = cf.get('mqtt_topic_prefix', 'broadlink/')


# noinspection PyUnusedLocal
def on_message(client, device, msg):
    command = msg.topic[len(topic_prefix):]

    if command == 'temperature' or command == 'energy':  # internal notification
        return

    try:
        action = str(msg.payload)
        logging.debug("Received MQTT message " + msg.topic + " " + action)

        if command == 'power':
            if device.type == 'SP1' or device.type == 'SP2':
                state = action == 'on'
                logging.debug("Setting power state to {0}".format(state))
                device.set_power(1 if state else 0)
                return

            if device.type == 'MP1':
                parts = action.split("/", 2)
                if len(parts) == 2:
                    sid = int(parts[0])
                    state = parts[1] == 'on'
                    logging.debug("Setting power state of socket {0} to {1}".format(sid, state))
                    device.set_power(sid, state)
                    return

        if device.type == 'RM2':
            file = dirname + "commands/" + command

            if action == '' or action == 'auto':
                record_or_replay(device, file)
                return
            elif action == 'record':
                record(device, file)
                return
            elif action == 'replay':
                replay(device, file)
                return
            elif action == 'macro':
                file = dirname + "macros/" + command
                macro(device, file)
                return

        logging.debug("Unrecognized MQTT message " + action)
    except Exception:
        logging.exception("Error")


# noinspection PyUnusedLocal
def on_connect(client, device, flags, result_code):
    topic = topic_prefix + '#'
    logging.debug("Connected to MQTT broker, subscribing to topic " + topic)
    mqttc.subscribe(topic, qos)


# noinspection PyUnusedLocal
def on_disconnect(client, device, rc):
    logging.debug("OOOOPS! Broadlink disconnects")
    time.sleep(10)


def record_or_replay(device, file):
    if os.path.isfile(file):
        replay(device, file)
    else:
        record(device, file)


def record(device, file):
    logging.debug("Recording command to file " + file)
    # receive packet
    device.enter_learning()
    ir_packet = None
    attempt = 0
    while ir_packet is None and attempt < 6:
        time.sleep(5)
        ir_packet = device.check_data()
        attempt = attempt + 1
    if ir_packet is not None:
        # write to file
        directory = os.path.dirname(file)
        if not os.path.exists(directory):
            os.makedirs(directory)
        with open(file, 'wb') as f:
            f.write(str(ir_packet).encode('hex'))
        logging.debug("Done")
    else:
        logging.warn("No command received")


def replay(device, file):
    logging.debug("Replaying command from file " + file)
    with open(file, 'rb') as f:
        ir_packet = f.read()
    device.send_data(ir_packet.decode('hex'))


def macro(device, file):
    logging.debug("Replaying macro from file " + file)
    with open(file, 'rb') as f:
        for line in f:
            line = line.strip(' \n\r\t')
            if len(line) == 0 or line.startswith("#"):
                continue
            if line.startswith("pause "):
                pause = int(line[6:].strip())
                logging.debug("Pause for " + str(pause) + " milliseconds")
                time.sleep(pause / 1000.0)
            else:
                command_file = dirname + "commands/" + line
                replay(device, command_file)


def get_device(cf):
    device_type = cf.get('device_type', 'lookup')
    if device_type == 'lookup':
        local_address = cf.get('local_address', None)
        lookup_timeout = cf.get('lookup_timeout', 20)
        devices = broadlink.discover(timeout=lookup_timeout) if local_address is None else \
            broadlink.discover(timeout=lookup_timeout, local_ip_address=local_address)
        if len(devices) == 0:
            logging.error('No Broadlink device found')
            sys.exit(2)
        if len(devices) > 1:
            logging.error('More than one Broadlink device found (' + ', '.join([d.host for d in devices]) + ')')
            sys.exit(2)
        return devices[0]
    elif device_type == 'test':
        return TestDevice(cf)
    else:
        host = (cf.get('device_host'), 80)
        mac = bytearray.fromhex(cf.get('device_mac').replace(':', ' '))
        if device_type == 'rm':
            return broadlink.rm(host=host, mac=mac)
        elif device_type == 'sp1':
            return broadlink.sp1(host=host, mac=mac)
        elif device_type == 'sp2':
            return broadlink.sp2(host=host, mac=mac)
        elif device_type == 'a1':
            return broadlink.a1(host=host, mac=mac)
        elif device_type == 'mp1':
            return broadlink.mp1(host=host, mac=mac)
        else:
            logging.error('Incorrect device configured: ' + device_type)
            sys.exit(2)


def broadlink_rm_temperature_timer(scheduler, delay, device):
    scheduler.enter(delay, 1, broadlink_rm_temperature_timer, [scheduler, delay, device])

    try:
        temperature = str(device.check_temperature())
        topic = topic_prefix + "temperature"
        logging.debug("Sending RM temperature " + temperature + " to topic " + topic)
        mqttc.publish(topic, temperature, qos=qos, retain=retain)
    except:
        logging.exception("Error")


def broadlink_sp_energy_timer(scheduler, delay, device):
    scheduler.enter(delay, 1, broadlink_sp_energy_timer, [scheduler, delay, device])

    try:
        energy = str(device.get_energy())
        topic = topic_prefix + "energy"
        logging.debug("Sending SP energy " + energy + " to topic " + topic)
        mqttc.publish(topic, energy, qos=qos, retain=retain)
    except:
        logging.exception("Error")


class SchedulerThread(Thread):
    def __init__(self, scheduler):
        Thread.__init__(self)
        self.scheduler = scheduler

    def run(self):
        try:
            self.scheduler.run()
        except:
            logging.exception("Error")


if __name__ == '__main__':
    device = get_device(cf)
    device.auth()
    logging.debug('Connected to %s Broadlink device at %s' % (device.type, device.host))

    clientid = cf.get('mqtt_clientid', 'broadlink-%s' % os.getpid())
    # initialise MQTT broker connection
    mqttc = paho.Client(clientid, clean_session=cf.get('mqtt_clean_session', False), userdata=device)

    mqttc.on_message = on_message
    mqttc.on_connect = on_connect
    mqttc.on_disconnect = on_disconnect

    mqttc.will_set('clients/broadlink', payload="Adios!", qos=0, retain=False)

    # Delays will be: 3, 6, 12, 24, 30, 30, ...
    # mqttc.reconnect_delay_set(delay=3, delay_max=30, exponential_backoff=True)

    if cf.get('tls') == True:
        mqttc.tls_set(cf.get('ca_certs'), cf.get('certfile'), cf.get('keyfile'), tls_version=cf.get('tls_version'), ciphers=None)

    if cf.get('tls_insecure'):
        mqttc.tls_insecure_set(True)

    mqttc.username_pw_set(cf.get('mqtt_username'), cf.get('mqtt_password'))
    mqttc.connect(cf.get('mqtt_broker', 'localhost'), int(cf.get('mqtt_port', '1883')), 60)

    broadlink_rm_temperature_interval = cf.get('broadlink_rm_temperature_interval', 0)
    if device.type == 'RM2' and broadlink_rm_temperature_interval > 0:
        scheduler = sched.scheduler(time.time, time.sleep)
        scheduler.enter(broadlink_rm_temperature_interval, 1, broadlink_rm_temperature_timer,
                        [scheduler, broadlink_rm_temperature_interval, device])
        # scheduler.run()
        tt = SchedulerThread(scheduler)
        tt.daemon = True
        tt.start()

    broadlink_sp_energy_interval = cf.get('broadlink_sp_energy_interval', 0)
    if device.type == 'SP2' and broadlink_sp_energy_interval > 0:
        scheduler = sched.scheduler(time.time, time.sleep)
        scheduler.enter(broadlink_sp_energy_interval, 1, broadlink_sp_energy_timer,
                        [scheduler, broadlink_sp_energy_interval, device])
        # scheduler.run()
        tt = SchedulerThread(scheduler)
        tt.daemon = True
        tt.start()

    while True:
        try:
            mqttc.loop_forever()
        except socket.error:
            time.sleep(5)
        except KeyboardInterrupt:
            sys.exit(0)
        except:
            logging.exception("Error")


The config file :slight_smile:

# Type of device. Valid options are 'lookup', 'rm', 'sp1', 'sp2', 'a1', 'mp1'
device_type = 'rm' # use lookup

## lookup parameters
lookup_timeout = 20
#local_address = '127.0.0.1'

## parameters for direct connection
device_host = '192.168.1.107'
device_mac  = '34:EA:34:51:xx.xx'

## MQTT connection parameters
mqtt_broker = 'localhost'       # default: 'localhost'
mqtt_port = 1883                # default: 1883
mqtt_clientid = 'mqtt_mini_2'
mqtt_username = ''
mqtt_password = ''
mqtt_topic_prefix = 'broadlink/mqtt_mini_2/'


#/etc/openhab2/scripts/MQTT/broadlink/mqtt_mini_2/mqtt.conf

## MQTT TLS parameters
# Required with TLS: a string path to the Certificate Authority certificate files that are to be treated as trusted by this client.
# ca_certs = '/path/to/ca_certsfile'
# Optional Clients Cert/Key
# certfile = '/path/to/certfile'
# keyfile  = '/path/to/keyfile'
# Required TLS version. Valid values: 'sslv3', 'tlsv1', 'tlsv1.2'
# tls_version = 'tlsv1.2'

## extra parameters
#broadlink_rm_temperature_interval = 120 # publish temperature from RM device to broadlink/temperature topic every two minutes
#broadlink_sp_energy_interval = 30 # publish energy from SP device to broadlink/energy topic every 30 seconds

create a service under /usr/lib/systemd/system

with this content :slight_smile:

[Unit]
Description=Starts and stops the Broadink Python script
Documentation=https://github.com/psyciknz/broadlink-mqtt
Wants=network-online.target
After=network-online.target

[Service]
EnvironmentFile=/etc/openhab2/scripts/MQTT/broadlink/mqtt_mini_2/mqtt.conf
User=openhab
Group=openhab
WorkingDirectory=/etc/openhab2/scripts/MQTT/broadlink/mqtt_mini_2/
PermissionsStartOnly=true
ExecStart=/usr/bin/python /etc/openhab2/scripts/MQTT/broadlink/mqtt_mini_2/mqtt_mini_2.py
# Shutdown delay in seconds, before process is tried to be killed with KILL (if configured)
TimeoutStopSec=20
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target

example of an item:
String Clim_3 “AC”

rule "ac noya"
when
    Item Clim_3 received command
then 

        switch (receivedCommand) 
        {
		case "on" :
                        publish("mosquitto", "broadlink/mqtt_mini_2/ac/on", "replay") 
		case "off" :
                        publish("mosquitto", "broadlink/mqtt_mini_2/ac/off", "replay") 

		case "heat" :
                        publish("mosquitto", "broadlink/mqtt_mini_2/ac/heat", "replay") 
                case "cold" :
                        publish("mosquitto", "broadlink/mqtt_mini_2/ac/cold", "replay") 
                case "plus" :
                        publish("mosquitto", "broadlink/mqtt_mini_2/ac/plus", "replay") 
                case "minus" :
                        publish("mosquitto", "broadlink/mqtt_mini_2/ac/minus", "replay")
         }
end

Hi guys, My RM2 inform the temperature 0. Anybody have this problem?
iR is working correctly e the temperature is above 20ºC.
In my network have one RM2 and one rm3 mini.

image

Were you able to control the somfy shutter with the broadlink RM pro?

I’ve got 2 RM2’s, that the app tells me are slightly different (one is a ‘Pro’, and one is ‘Pro Plus’, or something). One has temperature, and the other doesn’t.

The one with temperature isn’t really usable anyway, because every now and then it gives a random very high or random very low reading. I gave up on it for temperature and got another device.

BlackBeanControl is great for RM mini. It works like a charm. But does not seem to be suitable for SPx sockets. Does anybody know how to control them without the binding?

+1 for this. Have a harmony hube, but looking to buy a couple RM Pros due to the cost mainly and the added benefit of RF

At least from the docs these are supported by broadlink-mqtt

Works fine with openHAB & mqtt.

Hi

Where did you get aircond cool off and aircond cool on

Is it from mapping?

Hi Cato,

I’m very interested in your Broadlink binding. Is it still available?

Many thanks and all the best,

Jens

Thanks very much indeed! I’m a newbee, but I’ll try to work out on my own how to install and use this before I bug you any further.

Much appreciated!

Jens

Hello, I have several Broadlink devices (SC1 Broadlink Intelligent Switch, S2 Hub Alarm Kit, RM PRO and RM Mini), I also have Livolo RF switches. I control all equipment by IR / RF.

I would like to try this integration with openhab. I downloaded the file, but it is a file in JAVA extension and when I run it nothing happens. Can you give me some tips to help?

Thank you very much.

put it in addons folder

conf/addons

and you will see it during runtime

I would like a pm please. I cant seem to figure out the keys.