×
Recipes Smarthome

Smarthome | Apr 24, 2020

How to control LG A/C on OpenHAB using IR

LG AC with Sonoff IR remote control connected to OpenHAB

LG Air Conditioners come in two types. One comes with Wi-Fi connectivity (Deluxe modal with 4 way swing) and other modal does not come with any inbuilt Wi-Fi. With or without Wi-Fi still there is no OpenHAB bindling for LG A/C machines. However, both modals could be controlled with Infrared very easily with the help of MQTT.

LG A/C IR protocol

LG A/C infrared protocol is using a very reliable type of communication (most of the other A/C manufacturers other than LG too using a similar protocol) . The difference with A/C IR codes is a single IR code signal usually contains many properties such as A/C mode, temperature and fan speed. Therefore, current state such as current set temperature is not necessary to know to set a new temperature value. Because the value is sent in the IR signal.

Prerequisites

To continue with this implementation, below prerequisites are needed.

  • Sonoff device flashed with Tasmota 6.6.0 with MQTT configured
  • An IR emitter LED
  • OpenHAB installation with already setup MQTT binding
  • MQTT server

Set up the Remote control

In this case an ESP8266 based device with Tasmota installed is used (Sonoff TH10) to send the IR signal to A/C machine. Sonoff device can be wall mounted and connected to an IR emitter in such a way the IR signal reaches the A/C machine. It is possible to use a nodeMCU or a Raspberry Pi for the same purpose too. But Sonoff is compact, comes in good package and can be connected to A/C current out of the box. For help on installing Tasmota on Sonoff please refer to How to flash Sonoff devices using Arduino UNO.

How to connect IR LED to Sonoff
Connect GND to negative and RX to positive pin of LED

Please make sure Tasmota version 6.6.0 is installed and NOT any later version. The newer versions of Tasmota fail to send raw IR codes for some reason. Tasmota 6.6.0 has everything needed to get the thing done.

Use a 2 way IR transmitter module or a single 2 pin IR LED

Connect the IR LED positive to Sonoff RX pin and the negative pin to Sonoff GND pin. Then power up the Sonoff, go to Configuration then Configure Module.

Sonoff GPIO configuration for IRSend

Chnage GPIO3 Serial In feild to IRSend (8). Also don’t forget to configure MQTT on your Sonoff device as it can communicate with your OpenHAB installation.

Sonoff MQTT configuration

Testing Sonoff IR remote control

Open the Console from Sonoff Tasmota main menu and then type below command and press enter.

IRSEND 0,8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,1530,520,1530,520,520,520,520,520,520,520,520,520,520,520,1530,520,1530,520,1530,520

This is the IR command for turning on the A/C. Therefore, now if the A/C turns on then your wiring and LED placement is OK. If not please move the LED more closer to A/C machine or relocate the LED in such a way it can communicate with the A/C machine. A cellphone camera can be used to check if LED is emitting any signal or not.

OpenHAB configuration for LG A/C

First add the new Sonoff to your mqtt.things file.

Thing topic sonoff_th10 "TH10" {
    Channels:
        Type switch : PowerSwitch  [ stateTopic="sonoff/sensors/th10/stat/POWER" , transformationPattern="JSONPATH:$.POWER" , commandTopic="sonoff/sensors/th10/cmnd/POWER", on="ON", off="OFF" ]
		Type string : ssid  "SSID"        [ stateTopic="sonoff/sensors/th10/tele/STATE", transformationPattern="JSONPATH:$.Wifi.SSId"]
		Type number : rssi  "RSSI"        [ stateTopic="sonoff/sensors/th10/tele/STATE", transformationPattern="JSONPATH:$.Wifi.RSSI"]
		Type string : ip_addr  "IP"        [ stateTopic="sonoff/sensors/th10/tele/INFO2", transformationPattern="JSONPATH:$.IPAddress"]
		Type number : temperature  "Temperature"        [ stateTopic="sonoff/sensors/th10/tele/SENSOR", transformationPattern="JSONPATH:$.SI7021.Temperature"]
		Type number : humidity  "Humidity"        [ stateTopic="sonoff/sensors/th10/tele/SENSOR", transformationPattern="JSONPATH:$.SI7021.Humidity"]
		Type datetime : update  "Update"        [ stateTopic="sonoff/sensors/th10/tele/SENSOR", transformationPattern="JSONPATH:$.Time"]
		Type string : realtime_status  "State"        [ stateTopic="sonoff/sensors/th10/tele/STATE", transformationPattern="JSONPATH:$.POWER"]
		Type string : online_status  "Status"        [ stateTopic="sonoff/sensors/th10/tele/LWT"]
      }

Then create an ir.items file in items folder with below content.

Group    	grp_ir         	 "IR A/C Master Bed"      		<remotecontrol>        (Home)                   ["IR"]
String   	ir_acmbr_action	         "A/C Action [%s]"             <none>         (grp_ir)           {channel=""}  
String   	ir_acmbr_fan	         "A/C Fan [%s]"             <none>         (grp_ir)           {channel=""}  
String   	ir_acmbr_temp	         "A/C Temerature"             <snowflake2>         (grp_ir)           {channel=""}  
Number   	ir_acmbr_temp_n	         "A/C Temerature [%d]"             <none>         (grp_ir)           {channel=""}  
String   	ir_acmbr_temp_str	     "A/C Temerature"             <none>         (grp_ir)           {channel=""}  
Switch   	ir_acmbr_power	         "A/C Power"             <ac>        (grp_ir)           {channel=""}   
Switch   	ir_acmbr_swing	         "A/C Swing"             <none>         (grp_ir)           {channel=""}   
Switch   	ir_acmbr_hicool	         "A/C Hi-Cool"             <none>         (grp_ir)           {channel=""}  
DateTime 	ir_acmbr_on				"A/C on [%1$tb-%1$te %1$tH:%1$tM:%1$tS]" <none> (grp_ir)           {channel=""} 
DateTime 	ir_acmbr_off			"A/C off [%1$tb-%1$te %1$tH:%1$tM:%1$tS]" <none> (grp_ir)           {channel=""} 

//remote network information
String 		onl_th10 "Status [%s]"   <none> (grp_sensors) { channel="mqtt:topic:myMQTT:sonoff_th10:online_status" }
String 		ssid_th10 "SSID [%s]"   <none> (grp_sensors) { channel="mqtt:topic:myMQTT:sonoff_th10:ssid" }
String 		ip_th10 "IP [%s]"   <none> (grp_sensors) { channel="mqtt:topic:myMQTT:sonoff_th10:ip_addr" }
Number 		rssi_th10 "Signal [%d %%]"   <none> (grp_sensors) { channel="mqtt:topic:myMQTT:sonoff_th10:rssi" }

grp_ir is the Group for all items related to this A/C unit. Also this creates variables to display and keep values for A/C fan speed, temperature, power state and also for information on network state of the remote. Please make sure these item names do not contradict with your other items in OpenHAB.

Now add the remote to your OpenHAB sitemap.

Group item=onl_th10 label="A/C Remote" icon="remotecontrol" valuecolor=[Online="lime",!=Online="red"] visibility=[swi_master_bed_ac==ON] labelcolor=["yellow"] {
	Frame {
		Default item=ir_acmbr_power label="Power"
		Selection item=ir_acmbr_temp label="Temperature" mappings=[20="20°C",21="21°C",22="22°C",23="23°C",24="24°C",25="25°C",26="26°C",27="27°C",28="28°C"] visibility=[ir_acmbr_hicool==OFF]
		Default item=ir_acmbr_swing label="Swing" visibility=[ir_acmbr_hicool==OFF]
		Default item=ir_acmbr_action label="IR code" valuecolor=["maroon"]
		Default item=th10_temperature label="Room temperature"
		Default item=th10_humidity label="Humidity"
		//Default item=current_master_bed_ac label="Current"
		Default item=ir_acmbr_on 
		Default item=ir_acmbr_off
	}
	Frame label="Automation" {
		Default item=ir_acmbr_hicool label="H'cool (20°C high)"  labelcolor=[ON="aqua"]
	}
	Frame {
		Default item=onl_th10 label="Status"
		Default item=ssid_th10 label="SSID"
		Default item=rssi_th10 label="Signal"
		Default item=ip_th10 label="IP"
	}
}

Please use your own icons and rename the icons properly in Sitemap.

OpenHAB rules to control LG A/C

Finally create ir.rules rules file in the OpenHAB rules folder.

var String irCommand = "AC_OFF"
var String acTemp	= "26"
var String acFan	= "CHAOS"
var String acSwing = "SWING_ON"
var Timer acHighCoolTimer = null

rule "ir_acmbr_var_temp"
when
	Item ir_acmbr_temp received update
then
	acTemp = ir_acmbr_temp.state.toString
	irCommand = "AC_"+acFan+"_"+acTemp
	ir_acmbr_action.postUpdate(irCommand)
	ir_acmbr_temp_n.postUpdate(Float::parseFloat(String::format("%s",ir_acmbr_temp.state).replace(',','.')))
	
end

rule "ir_acmbr_var_fan"
when
	Item ir_acmbr_fan received update
then
	acFan = ir_acmbr_fan.state.toString
	irCommand = "AC_"+acFan+"_"+acTemp
	ir_acmbr_action.postUpdate(irCommand)
end

rule "ir_acmbr_var_swing"
when
	Item ir_acmbr_swing received update
then
	if (ir_acmbr_swing.state == ON) {
		ir_acmbr_action.postUpdate("SWING_ON")
	}
	else {
		ir_acmbr_action.postUpdate("SWING_OFF")
	}
end

rule "ir_acmbr_var_hicool"
when
	Item ir_acmbr_hicool received update
then
	if (ir_acmbr_hicool.state == ON) {
		ir_acmbr_action.postUpdate("AC_HIGH_20")
	}
	else {
		ir_acmbr_action.postUpdate("AC_"+acFan+"_"+acTemp)
	}
end

rule "ir_acmbr_ir_send"
when
    Item ir_acmbr_action received update
then
	val mqttActions = getActions("mqtt", "mqtt:broker:myMQTT")
	var String ir_send = ""
	
	var String AC_ON = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,1530,520,1530,520,520,520,520,520,520,520,520,520,520,520,1530,520,1530,520,1530,520"
	var String AC_OFF = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,520,520,520,520,1530,520"
	
	var String AC_CHAOS_20 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,520,520,520,520,1530,520,520,520"
	var String AC_CHAOS_21 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,1530,520,520,520,520,520,1530,520,520,520,1530,520,520,520,520,520,1530,520,1530,520"
	var String AC_CHAOS_22 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,1530,520,1530,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,520,520,520,520"
	var String AC_CHAOS_23 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,520,520,1530,520"
	var String AC_CHAOS_24 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,1530,520,520,520"
	var String AC_CHAOS_25 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,1530,520,1530,520"
	var String AC_CHAOS_26 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,520,520,1530,520,1530,520,520,520,1530,520,520,520,1530,520,1530,520,520,520,520,520,520,520"
	var String AC_CHAOS_27 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,1530,520,520,520,520,520,520,520,1530,520,520,520,1530,520,1530,520,520,520,520,520,1530,520"
	var String AC_CHAOS_28 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,1530,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,1530,520,520,520,1530,520,520,520"
	var String AC_LOW_20 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,1530,520,520,520,1530,520"
	var String AC_LOW_21 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,1530,520,520,520,520,520,520,520,520,520,520,520,1530,520,1530,520,1530,520,520,520"
	var String AC_LOW_22 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,1530,520,1530,520,520,520,520,520,520,520,520,520,1530,520,1530,520,1530,520,1530,520"
	var String AC_LOW_23 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520"
	var String AC_LOW_24 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520"
	var String AC_LOW_25 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520"
	var String AC_LOW_26 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,520,520,1530,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,1530,520"
	var String AC_LOW_27 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520"
	var String AC_LOW_28 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,1530,520,1530,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,1530,520"
	
	var String SWING_ON = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,1530,520,1530,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,520,520,1530,520,520,520,520,520,1530,520"
	var String SWING_OFF = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,1530,520,1530,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,1530,520,520,520,1530,520,520,520"

	var String AC_HIGH_20 = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,1530,520,520,520,1530,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,1530,520"
	
	var String DEHUM_HIGH = "8900,4130,520,1530,520,520,520,520,520,520,520,1530,520,520,520,520,520,520,520,520,520,520,520,520,520,520,520,1530,520,520,520,520,520,1530,520,1530,520,520,520,520,520,1530,520,520,520,1530,520,520,520,520,520,520,520,1530,520,1530,520,520,520"
	
	
	if (ir_acmbr_action.state == "AC_ON") {
		ir_send = AC_ON
	}
	else if (ir_acmbr_action.state == "AC_OFF") {
		ir_send = AC_OFF
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_20") {
		ir_send = AC_CHAOS_20
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_21") {
		ir_send = AC_CHAOS_21
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_22") {
		ir_send = AC_CHAOS_22
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_23") {
		ir_send = AC_CHAOS_23
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_24") {
		ir_send = AC_CHAOS_24
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_25") {
		ir_send = AC_CHAOS_25
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_26") {
		ir_send = AC_CHAOS_26
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_27") {
		ir_send = AC_CHAOS_27
	}
	else if (ir_acmbr_action.state == "AC_CHAOS_28") {
		ir_send = AC_CHAOS_28
	}
	else if (ir_acmbr_action.state == "AC_LOW_20") {
		ir_send = AC_LOW_20
	}
	else if (ir_acmbr_action.state == "AC_LOW_21") {
		ir_send = AC_LOW_21
	}
	else if (ir_acmbr_action.state == "AC_LOW_22") {
		ir_send = AC_LOW_22
	}
	else if (ir_acmbr_action.state == "AC_LOW_23") {
		ir_send = AC_LOW_23
	}
	else if (ir_acmbr_action.state == "AC_LOW_24") {
		ir_send = AC_LOW_24
	}
	else if (ir_acmbr_action.state == "AC_LOW_25") {
		ir_send = AC_LOW_25
	}
	else if (ir_acmbr_action.state == "AC_LOW_26") {
		ir_send = AC_LOW_26
	}
	else if (ir_acmbr_action.state == "AC_LOW_27") {
		ir_send = AC_LOW_27
	}
	else if (ir_acmbr_action.state == "AC_LOW_28") {
		ir_send = AC_LOW_28
	}
	else if (ir_acmbr_action.state == "AC_HIGH_20") {
		ir_send = AC_HIGH_20
	}
	
	else if (ir_acmbr_action.state == "SWING_ON") {
		ir_send = SWING_ON
	}
	else if (ir_acmbr_action.state == "SWING_OFF") {
		ir_send = SWING_OFF
	}
	
	else if (ir_acmbr_action.state == "DEHUM_HIGH") {
		ir_send = DEHUM_HIGH
	}
	
	mqttActions.publishMQTT("sonoff/sensors/th10/cmnd/IRSEND", "0,"+ir_send)
end

rule "ir_acmbr_power"
when
    Item ir_acmbr_power received update
then
	if (ir_acmbr_power.state == ON) {
		ir_acmbr_action.postUpdate("AC_ON")
		ir_acmbr_on.postUpdate(now.toString)
		Thread::sleep(1000)
		irCommand = "AC_"+ir_acmbr_fan.state.toString+"_"+ir_acmbr_temp.state.toString
		ir_acmbr_action.postUpdate(irCommand)
	}
	else if (ir_acmbr_power.state == OFF){
		ir_acmbr_action.postUpdate("AC_OFF")
		ir_acmbr_off.postUpdate(now.toString)
	}
end

rule "ir_acmbr_quick_cool_on"
when
	Item ir_acmbr_hicool changed to ON
then
	if (swi_master_bed_ac.state == ON && ir_acmbr_power.state == ON) {
		if(acHighCoolTimer === null || acHighCoolTimer.hasTerminated()) {
			acHighCoolTimer = createTimer(now.plusMinutes(30), [ |
				ir_acmbr_hicool.postUpdate(OFF)
				acHighCoolTimer = null
			])
		}
		else {
			acHighCoolTimer.reschedule(now.plusMinutes(30))
		}
	}
end

This code is written assuming your MQTT broker is myMQTT. Please alter any values in code based on your configuration. LG A/C IR codes decoded by nokru for LG AC LIRC project on GitHub are used in above code.

Connect LG AC to OpenHAB

When Sonoff POW R2 and the TH10 is used for above configuration, properties such as room temperature, humidity and the power usage can be measured and added to OpenHAB.

If it is necessary to measure the room temperature or humidity use a TH10 or a different device for that, but not Sonoff POW R2. You should never connect any sensors to Sonoff POW R2 as POW R2 has A/C current on its GPIOs.

In this OpenHAB remote, it is possible to Power on and off, set the Temperature, change the Fan speed and turn on or off the Swing. H’Cool option will change the Temperature to 20°C with full fan speed for 30 minutes and will turn off automatically. Also you can write your own rules to automate your A/C smarter than LG’s own controller module.

Leave a Reply

Your email address will not be published. Required fields are marked *