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

I’m only using the A1 sensor, so no hub, work OK though.

Any way to use this with homekit ? I can send command on Basic UI but not on iOS

Ok circling back this was a mis configured rule that had multiple when conditions that within the rule retriggered the same rule causing a circular rule and quickly running out of memory. Binding is working great

Hello,

I can not access the tutorial you created, and it helped me to set up my Broadlink RM3 mini.
Could you tell me if you have posted it in another url?

Thanks

do you have Homekit configured in OH?

Hi everyone,

I was wondering if there is a way not to use the map-file with this binding. I am creating the IR codes for my Daikin air conditioner at runtime using a rule, so I do not want to create a key for each configuration.

rule "AC control"
when Member of AC received command
then 
    val low_str  = '0e0e'
    val high_str = '0e2c'

    val prefix = "260050020e0e0e0e0e0e0e0e0e0e0d000341713a0e2c0e0e0e0e0e0e0e2c0e0e0e0e0e0e0e0e0e2c0e0e0e2c0e2c0e0e0e2c0e2c0e2c0e2c0e2c0e0e0e0e0e2c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e2c0e0e0e2c0e0e0e0e0e0e0e2c0e2c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e2c0e2c0e2c0e0e0e2c0e0e0e2c0e2c0d000474713a0e2c0e0e0e0e0e0e0e2c0e0e0e0e0e0e0e0e0e2c0e0e0e2c0e2c0e0e0e2c0e2c0e2c0e2c0e2c0e0e0e0e0e2c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e2c0e0e0e0e0e0e0e0e0e2c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e2c0e0e0e2c0e0e0e2c0e0e0d000475713a"    
    val header = "0e2c0e0e0e0e0e0e0e2c0e0e0e0e0e0e0e0e0e2c0e0e0e2c0e2c0e0e0e2c0e2c0e2c0e2c0e2c0e0e0e0e0e2c0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e0e"
    val suffix = "0d000d050000000000000000"

    var power = AC_power.state()
    val mode = AC_mode.state()
    val temp_set = AC_set_temp.state()
    val wind_level = AC_windlevel.state()   
    val wind_swing = AC_windswing.state()
    val powermode = AC_powermode.state()
    val economy = AC_economy.state()

    logInfo("broadlink.rules", "State: power "+power+", mode "+mode+", temp "+temp_set+", wind_level "+wind_level + ", wind_swing " + wind_swing + ", powermode " + powermode + ", economy " + economy)
    
    //Integer::parseInt(str,16).intValue()

    // Convert to int for checksum: 11 + +da + 27 + 00 + 00
    // header
    val header_int = Integer.parseInt("11", 16) + Integer.parseInt("da", 16) + Integer.parseInt("27", 16)
    
    
    var mode_byte_hex = mode.toString
    if (mode == NULL){
        mode_byte_hex = "0"
        postUpdate(AC_mode,0)
    } 
    if (power == ON){
        mode_byte_hex += "9"
    } else {
        mode_byte_hex += "8"
        postUpdate(AC_power,OFF)
        power = OFF
    }
    if (power == OFF){
        postUpdate(AC_mode_google,"off")
    } else if (mode_byte_hex.substring(0,1) == "0"){
        postUpdate(AC_mode_google,"heatcool")
    } else if (mode_byte_hex.substring(0,1) == "3"){
        postUpdate(AC_mode_google,"cool")
    } else if (mode_byte_hex.substring(0,1) == "4"){
        postUpdate(AC_mode_google,"heat")
    } else {
        postUpdate(AC_mode_google,"on")
    }

    val mode_byte_int = Integer.parseInt(mode_byte_hex, 16)
    var mode_byte_bin = Integer::toBinaryString(mode_byte_int)
    var mode_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < mode_byte_bin.length){
            mode_byte_cor += mode_byte_bin.charAt(mode_byte_bin.length-i-1)
        } else {
            mode_byte_cor += "0"
        } 
    }
    mode_byte_bin = String::format("%1$8s", mode_byte_bin)
    logInfo("broadlink.rules", "mode_byte_hex "+mode_byte_hex)
    logInfo("broadlink.rules", "mode_byte_cor "+mode_byte_cor)

       
    val temp_byte_int = 40
    if ((temp_set != null) && (temp_set != NULL)){
        temp_byte_int = 2*Integer.parseInt(temp_set.toString)
    } else {
        postUpdate(AC_set_temp,20)
    }
    val temp_byte_hex = Integer::toHexString(temp_byte_int)
    val temp_byte_bin = Integer::toBinaryString(temp_byte_int)
    var temp_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < temp_byte_bin.length){
            temp_byte_cor += temp_byte_bin.charAt(temp_byte_bin.length-i-1)
        } else {
            temp_byte_cor += "0"
        } 
    }
    logInfo("broadlink.rules", "temp_byte_hex "+temp_byte_hex)
    logInfo("broadlink.rules", "temp_byte_cor "+temp_byte_cor)


    val fixed_byte_hex = "00"
    val fixed_byte_int = Integer.parseInt(fixed_byte_hex, 16)
    val fixed_byte_bin = Integer::toBinaryString(fixed_byte_int)
    var fixed_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < fixed_byte_bin.length){
            fixed_byte_cor += fixed_byte_bin.charAt(fixed_byte_bin.length-i-1)
        } else {
            fixed_byte_cor += "0"
        } 
    }

    var wind_byte_hex = "a"
    if (wind_level == 0){
        wind_byte_hex = "a"
    } else if (wind_level == 1){
        wind_byte_hex = "b"
    } else if ((wind_level != null) && (wind_level != NULL)){
        wind_byte_hex = wind_level.toString
    } else {
        postUpdate(AC_windlevel,0)
    }
    if (wind_swing == ON){
        wind_byte_hex += "f"
    } else {
        wind_byte_hex += "0"
    }
    val wind_byte_int = Integer.parseInt(wind_byte_hex, 16)
    val wind_byte_bin = Integer::toBinaryString(wind_byte_int)
    var wind_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < wind_byte_bin.length){
            wind_byte_cor += wind_byte_bin.charAt(wind_byte_bin.length-i-1)
        } else {
            wind_byte_cor += "0"
        } 
    }
    logInfo("broadlink.rules", "wind_byte_hex "+wind_byte_hex)
    logInfo("broadlink.rules", "wind_byte_cor "+wind_byte_cor)
    
    val timer_a_byte_hex = "00"
    val timer_b_byte_hex = "06"
    val timer_c_byte_hex = "60"
    val timer_a_byte_int = Integer.parseInt(timer_a_byte_hex, 16)
    val timer_b_byte_int = Integer.parseInt(timer_b_byte_hex, 16)
    val timer_c_byte_int = Integer.parseInt(timer_c_byte_hex, 16)
    val timer_a_byte_bin = Integer::toBinaryString(timer_a_byte_int)
    val timer_b_byte_bin = Integer::toBinaryString(timer_b_byte_int)
    val timer_c_byte_bin = Integer::toBinaryString(timer_c_byte_int)
    var timer_a_byte_cor = ""
    var timer_b_byte_cor = ""
    var timer_c_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < timer_a_byte_bin.length){
            timer_a_byte_cor += timer_a_byte_bin.charAt(timer_a_byte_bin.length-i-1)
        } else {
            timer_a_byte_cor += "0"
        }
        if (i < timer_b_byte_bin.length){
            timer_b_byte_cor += timer_b_byte_bin.charAt(timer_b_byte_bin.length-i-1)
        } else {
            timer_b_byte_cor += "0"
        } 
        if (i < timer_c_byte_bin.length){
            timer_c_byte_cor += timer_c_byte_bin.charAt(timer_c_byte_bin.length-i-1)
        } else {
            timer_c_byte_cor += "0"
        }
    }
    logInfo("broadlink.rules", "timer_byte_hex "+timer_a_byte_hex + " " +timer_b_byte_hex + " " + timer_c_byte_hex)
    logInfo("broadlink.rules", "timer_byte_cor "+timer_a_byte_cor + " " +timer_b_byte_cor + " " + timer_c_byte_cor)

    
    var powermode_byte_hex = "00"
    if (powermode == ON){
        powermode_byte_hex = "01"
    }
    val powermode_byte_int = Integer.parseInt(powermode_byte_hex, 16)
    val powermode_byte_bin = Integer::toBinaryString(powermode_byte_int)
    var powermode_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < powermode_byte_bin.length){
            powermode_byte_cor += powermode_byte_bin.charAt(powermode_byte_bin.length-i-1)
        } else {
            powermode_byte_cor += "0"
        } 
    }
    logInfo("broadlink.rules", "powermode_byte_hex "+powermode_byte_hex)
    logInfo("broadlink.rules", "powermode_byte_cor "+powermode_byte_cor)


    val special_byte_hex = "c1"
    val special_byte_int = Integer.parseInt(special_byte_hex, 16)
    val special_byte_bin = Integer::toBinaryString(special_byte_int)
    var special_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < special_byte_bin.length){
            special_byte_cor += special_byte_bin.charAt(special_byte_bin.length-i-1)
        } else {
            special_byte_cor += "0"
        } 
    }

    var economy_byte_hex = "c1"//80
    if (economy == ON){
        economy_byte_hex = "c1"//84
    }
    val economy_byte_int = Integer.parseInt(economy_byte_hex, 16)
    val economy_byte_bin = Integer::toBinaryString(economy_byte_int)
    var economy_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < economy_byte_bin.length){
            economy_byte_cor += economy_byte_bin.charAt(economy_byte_bin.length-i-1)
        } else {
            economy_byte_cor += "0"
        } 
    }
    logInfo("broadlink.rules", "economy_byte_hex "+economy_byte_hex)
    logInfo("broadlink.rules", "economy_byte_cor "+economy_byte_cor)

    val check_byte_int_full = header_int + mode_byte_int+temp_byte_int+wind_byte_int+timer_a_byte_int+timer_b_byte_int+timer_c_byte_int+powermode_byte_int+economy_byte_int
    val check_byte_hex_full = Integer::toHexString(check_byte_int_full)
    val check_byte_hex = check_byte_hex_full.substring(check_byte_hex_full.length-2,check_byte_hex_full.length)
    val check_byte_int = Integer.parseInt(check_byte_hex, 16)
    val check_byte_bin = Integer::toBinaryString(check_byte_int)
    var check_byte_cor = ""
    for (var i = 0; i<8; i++){
        if (i < check_byte_bin.length){
            check_byte_cor += check_byte_bin.charAt(check_byte_bin.length-i-1)
        } else {
            check_byte_cor += "0"
        } 
    }
    logInfo("broadlink.rules", "check_byte_hex_full "+check_byte_hex_full)
    logInfo("broadlink.rules", "check_byte_hex "+check_byte_hex)
    logInfo("broadlink.rules", "check_byte_cor "+check_byte_cor)
    
    val command_bin = mode_byte_cor + temp_byte_cor + fixed_byte_cor + wind_byte_cor + fixed_byte_cor + timer_a_byte_cor + timer_b_byte_cor + timer_c_byte_cor + powermode_byte_cor + fixed_byte_cor + economy_byte_cor + fixed_byte_cor + fixed_byte_cor + check_byte_cor
    logInfo("broadlink.rules", "command_bin "+command_bin)
    logInfo("broadlink.rules", "command_hex "+mode_byte_hex + temp_byte_hex + fixed_byte_hex + wind_byte_hex + fixed_byte_hex + timer_a_byte_hex + timer_b_byte_hex + timer_c_byte_hex + powermode_byte_hex + fixed_byte_hex + economy_byte_hex + fixed_byte_hex + special_byte_hex + check_byte_hex)
    
    val zero = '0'
    var control = ""
    for (var i = 0; i<command_bin.length; i++){
        if (command_bin.substring(i,i+1).equals(zero)){
            control += low_str
        } else {
            control += high_str
        }
    }
    
    val String commandStr = prefix+header+control+suffix
    logInfo("broadlink.rules", "Command send: "+commandStr)
    postUpdate(BroadlinkRM3_Command,commandStr)
    var String result = executeCommandLine("python /etc/openhab2/scripts/broadlink_cli --type 10178 --host 192.168.1.6 --mac 780f775191f6 --send " +commandStr,10000)
    logInfo("broadlink.rules", "python: "+result)

end

if there is a better solution than using a rule, that is welcome too.
Thanks

Edit:
For now I will use “executeCommandLine” as a work around

postUpdate(BroadlinkRM3_Command,commandStr)
var String result = executeCommandLine("python /etc/openhab2/scripts/broadlink_cli --type 10178 --host <ip-address> --mac <mac-address> --send " +commandStr,10000)

using the broadlink github repository by mjg59

Edit (2019-03-24):
I updated the posted rule to the one, which I am using now.

Furthermore the items file including google home integration:

Group AC "Air conditioner"
Group g_AC "Klimaanlage" [ "Thermostat", "Celsius" ]
  Switch AC_power "Klimaanlage" (AC)
  String AC_mode_google "Modus" (g_AC) [ "homekit:HeatingCoolingMode" ]
  String AC_mode "Modus" (AC)
  Number AC_cur_temp "Temperatur" (AC, g_AC) [ "CurrentTemperature" ] 
  Number AC_set_temp "Zieltemperatur" (AC, g_AC) [ "TargetTemperature" ]
  Number AC_windlevel "Wind" (AC)
  Switch AC_windswing "Windrichtung" (AC)
  Switch AC_powermode "Power modus" (AC)
  Switch AC_economy "Economy" (AC)

and the relevant part of my sitemap:

    Text label="Air Conditioner" icon=aircon
    {
        Switch item=AC_power label="Power" icon="heating"
        Setpoint item=AC_set_temp minValue=18 maxValue=32 step=1 icon="temperature" label="Setpoint [%.0f °C]"
        Switch item=AC_mode label="Mode" icon="sofa" mappings=[0="Auto", 2="Dry", 3="Cool", 4="Heat", 6="Fan"]
        Switch item=AC_windlevel label="Wind level" icon="fan" mappings=[0="Auto", 1= "Calm", 3="▂", 4="▃", 5="▄", 6="▆", 7="▇"]
        Switch item=AC_windswing label="Wind swing" icon="aircon_swing"
        Switch item=AC_powermode label="Power mode" icon="wind"
        Switch item=AC_economy label="Economy" icon="econmode"
    }

and a separate rule to change the mode key which is received from google home to the one used by the airconditioner:

rule "AC Google mode"
when Item AC_mode_google received command
then 
    var mode = AC_mode_google.state()
    if (mode == NULL){
        sendCommand(AC_mode, "0")
    } else if (mode == "heat"){
        postUpdate(AC_power, ON)
        sendCommand(AC_mode, "4")
    } else if (mode == "cool"){
        postUpdate(AC_power, ON)
        sendCommand(AC_mode, "3")
    } else if (mode == "heatcool"){
        postUpdate(AC_power, ON)
        sendCommand(AC_mode, "0")
    } else if (mode == "on"){
        sendCommand(AC_power, ON)
    } else if (mode == "off"){        
        sendCommand(AC_power, OFF)
    }
end

Maybe this helps someone else.

4 Likes

Hi! Thanks for the binding.
I’m not sure if I have the same problems as the others do, or it is some kind of network problem. So my RM3 mini (fw: v55) sometimes goes offline. When it is offline I cannot even ping it. After approxiately half a minute it comes back. For me it seems that it goes offline more frequently if I’m sending out IR signals (through this binding), but sometime it goes offline even when in idle. Do you think it is related the the binding or it is some kind of network issue?

My log is:
==> /var/log/openhab2/openhab.log <==

2019-03-30 10:58:02.677 [ERROR] [dlink.handler.BroadlinkRemoteHandler] - rm3:c8-f7-42-17-2a-89[^]: updateItemStatus: Online -> Offline

==> /var/log/openhab2/events.log <==

2019-03-30 10:58:02.702 [me.event.ThingUpdatedEvent] - Thing ‘broadlink:rm3:c8-f7-42-17-2a-89’ has been updated.

2019-03-30 10:58:02.747 [me.event.ThingUpdatedEvent] - Thing ‘broadlink:rm3:c8-f7-42-17-2a-89’ has been updated.

2019-03-30 10:58:02.752 [hingStatusInfoChangedEvent] - ‘broadlink:rm3:c8-f7-42-17-2a-89’ changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Could not find device at IP address 192.168.0.107

2019-03-30 10:58:32.788 [me.event.ThingUpdatedEvent] - Thing ‘broadlink:rm3:c8-f7-42-17-2a-89’ has been updated.

2019-03-30 10:58:32.827 [me.event.ThingUpdatedEvent] - Thing ‘broadlink:rm3:c8-f7-42-17-2a-89’ has been updated.

2019-03-30 10:58:32.855 [hingStatusInfoChangedEvent] - ‘broadlink:rm3:c8-f7-42-17-2a-89’ changed from OFFLINE (COMMUNICATION_ERROR): Could not find device at IP address 192.168.0.107 to ONLINE

Hi everyone,
I was wondering if there is a way not to use the map-file with this binding. I am creating the IR codes for my Daikin air conditioner at runtime using a rule, so I do not want to create a key for each configuration.

Thanks !

Yes, I can control my leds, but not broadlink.

i am using node red for rules but you can setup something like this a rule an item controlled by siri lets say TV on

Switch TVON “TV” (groupSiri) [ “Switchable” ]

than create a rule that will triger the IR command for the broadlink with that value (TVon = 35456343…)
and set it back to 0 again , if you are using node red i can give you a working example
dont know much about OH rules

1 Like

I am noob in node red. But I will try. Any tips where i start ?

its kind of a long trip… but totally worth it so dont give up

the best Guide out there i think…

in Short

  1. install Node-js
  2. Install node red
  3. download Openhab pallete for node red
  4. go throw the guide even twice ! i found myself too many times reaching to the same method that is in the guide already but i missed it :slight_smile:
  5. start watching youtube you will get a lot of ideas for your flows
1 Like

I have fixed powering off for all ports and powering on for 2, 3, 4 ports for Broadlink MP1.
How to contact the addon author?

Hello Dmytro
I think you mean the person who made the binding?, that would be @themillhousegroup themillhousegroup , John Marshall

Thank you. Also I have fixed status check for MP1.
I have created fork

1 Like

That would be great, I have 6 MP1’s and did not use the binding for the MP1’s because the problem you mentioned, with a reboot they all go off and than on again, not what you wan’t! :stuck_out_tongue_winking_eye:, so until now I’m using http to control my MP1’s
Do you have a .jar file, so I can test?
Ray

https://drive.google.com/open?id=1r-u1NARzXVMrw2iVG_M0NiFO5fY8LHfn

Nice. So as I see, you forked from a different repo. The repo to create a PR for would ideally be this one, which is actively developed: https://github.com/themillhousegroup/openhab2-addons/tree/master/addons/binding/org.openhab.binding.broadlink
Unfortunately I think @themillhousegroup is also rather busy atm, so it might take some time to get it merged. But that would be the repo with the most recent changes. Hopefully the whole repo can then be merged soon into the official openHAB repo, so the binding is officially available and contributions are easier to maintain.

1 Like

Can somebody please PM me the Authentication Key and IV values or the link please. I am unable to find it by googling.

Please see my post #552 - Broadlink binding for RMx, A1, SPx and MP. Any interest?