ESPhome IR Blaster example

Here’s my contribution to this great community.
An ESPhome based IR Blaster that can handle several protocols.
There is also an option to record IR codes from unknown devices or remotes.

To test this go to Developers tools - services
Select ESPHome: ir_blaster_send_ir
Filled the data and call service.

# IR Blaster
# by AA van Zoelen
#
# Version: 1.1
# Date   : 23 Aug 2022
#
# Description
# ===========
# Protocol : String in UPPERCASE
# Code     : Some protocols name this also data, address, wand_id, rc_code1
# Command  : Some protocols name this also nbits, magnitude, rc_code2
# Repeat   : Optionally set the code to be repeated a number of times. Otherwise use 1.
#
# Implemented protocols
# - COOLIX
# - DISH
# - JVC
# - LG
# - MAGIQUEST
# - NEC
# - PANASONIC
# - PIONEER (repeat 2 added)
# - PRONTO
# - RC5
# - RC6
# - SAMSUNG
# - SAMSUNG36
# - SONY
# - TOSHIBA_AC
#
# Details
# -------
# MCU used is a Wemos D1 mini
#
# Minimal components
# IR receiver such as TSOP38238 - IR receiver 
# NPN transistor (e.g. BC549C, 2n2222)
# IR LED
#
esphome:
  name: "ir-blaster"
  platform: ESP8266
  board: nodemcuv2

# Enable logging
logger:

# Enable Home Assistant API
api:
  services:
    - service: send_ir
      variables:
        protocol: string
        code: string
        command: string
        repeats: string
      then:
        - lambda: 'ESP_LOGD("main", "Protocol: %s - Code: %s - Command: %s",  protocol.c_str(), code.c_str(), command.c_str() ); '
        - if:
            condition: # --=[ *** COOLIX *** ]=--
              lambda: 'return protocol == "COOLIX";'
            then:
              - logger.log: "Sending COOLIX data...!"
              - remote_transmitter.transmit_coolix:
                  data: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i;
        - if:
            condition: # --=[ *** DISH *** ]=--
              lambda: 'return protocol == "DISH";'
            then:
              - logger.log: "Sending DISH data...!"
              - remote_transmitter.transmit_dish:
                  address: !lambda |-
                        return std::stoi( code );
                  command: !lambda |-
                        return std::stoi( command );
        - if:
            condition: # --=[ *** JVC *** ]=--
              lambda: 'return protocol == "JVC";'
            then:
              - logger.log: "Sending JVC data...!"
              - remote_transmitter.transmit_jvc:
                  data: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i;                      
        - if:
            condition: # --=[ *** LG *** ]=--
              lambda: 'return protocol == "LG";'
            then:
              - logger.log: "Sending LG data...!"
              - remote_transmitter.transmit_lg:
                  data: !lambda |-
                       int i;
                       sscanf(code.c_str(), "%x", &i );
                       return i;
                  nbits: !lambda |-
                        return std::stoi( command );
        - if:
            condition: # --=[ *** MAGIQUEST *** ]=--
              lambda: 'return protocol == "MAGIQUEST";'
            then:
              - logger.log: "Sending MAGIQUEST data...!"
              - remote_transmitter.transmit_magiquest:
                  wand_id: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i;
                  magnitude: !lambda |-
                      int i;
                      sscanf( command.c_str(), "%x", &i );
                      return i;
        - if:
            condition: # --=[ *** NEC *** ]=--
              lambda: 'return protocol == "NEC";'
            then:
              - logger.log: "Sending NEC data...!"
              - remote_transmitter.transmit_nec:
                  address: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i;
                  command: !lambda |-
                      int i;
                      sscanf( command.c_str(), "%x", &i );
                      return i;
                    
        - if:
            condition: # --=[ *** PANASONIC *** ]=--
              lambda: 'return protocol == "PANASONIC";'
            then:
              - logger.log: "Sending PANASONIC data...!"
              - remote_transmitter.transmit_panasonic:
                  address: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i; 
                  command: !lambda |-
                      int i;
                      sscanf( command.c_str(), "%x", &i );
                      return i;
        - if:
            condition: # --=[ *** PIONEER *** ]=--
              lambda: 'return protocol == "PIONEER";'
            then:
              - logger.log: "Sending PIONEER data...!"
              - remote_transmitter.transmit_pioneer:
                  rc_code_1: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i;
                  rc_code_2: !lambda |-
                      int i;
                      sscanf( command.c_str(), "%x", &i );
                      return i;
                  repeat:
                    times: !lambda |-
                        return std::stoi( repeats );
        - if:
            condition: # --=[ *** PRONTO *** ]=--
              lambda: 'return protocol == "PRONTO";'
            then:
              - logger.log: "Sending PRONTO data...!"
              - remote_transmitter.transmit_pronto:
                  data: !lambda |-
                      return code.c_str();
        - if:
            condition: # --=[ *** RC5 *** ]=--
              lambda: 'return protocol == "RC5";'
            then:
              - logger.log: "Sending RC5 data...!"
              - remote_transmitter.transmit_rc5:
                  address: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i; 
                  command: !lambda |-
                      int i;
                      sscanf( command.c_str(), "%x", &i );
                      return i;
        - if:
            condition: # --=[ *** RC6 *** ]=--
              lambda: 'return protocol == "RC6";'
            then:
              - logger.log: "Sending RC6 data...!"
              - remote_transmitter.transmit_rc6:
                  address: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i;
                  command: !lambda |-
                      int i;
                      sscanf( command.c_str(), "%x", &i );
                      return i;
        - if:
            condition: # --=[ *** SAMSUNG *** ]=--
              lambda: 'return protocol == "SAMSUNG";'
            then:
              - logger.log: "Sending SAMSUNG data...!"
              - remote_transmitter.transmit_samsung:
                  data: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i; 
                  nbits: !lambda |-
                        return std::stoi( command );   
        - if:
            condition: # --=[ *** SAMSUNG36 *** ]=--
              lambda: 'return protocol == "SAMSUNG36";'
            then:
              - logger.log: "Sending SAMSUNG36 data...!"
              - remote_transmitter.transmit_samsung36:
                  address: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i;
                  command: !lambda |-
                      int i;
                      sscanf( command.c_str(), "%x", &i );
                      return i;
        - if:
            condition: # --=[ *** SONY *** ]=--
              lambda: 'return protocol == "SONY";'
            then:
              - logger.log: "Sending SONY data...!"
              - remote_transmitter.transmit_sony:
                  data: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i; 
                  nbits: !lambda |-
                        return std::stoi( command );
        - if:
            condition: # --=[ *** TOSHIBA AC *** ]=--
              lambda: 'return protocol == "TOSHIBA_AC";'
            then:
              - logger.log: "Sending TOSHIBA data...!"
              - remote_transmitter.transmit_toshiba_ac:
                  rc_code_1: !lambda |-
                      int i;
                      sscanf( code.c_str(), "%x", &i );
                      return i;
                  rc_code_2: !lambda |-
                      int i;
                      sscanf( command.c_str(), "%x", &i );
                      return i;                        


ota:


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: !secret backdoor_ssid
    password: !secret backdoor_password

captive_portal:

remote_receiver:
  pin:
    number: D5
    inverted: true
  dump: all

  
remote_transmitter:
  pin: D7
  carrier_duty_percent: 50%

8 Likes

Love the idea. Could you include a schematic?

Here’s not a real schematic but it makes it clear what goes where.

This can be power from USB or by supplying 5V from a adapter.

1 Like

Very helpful, thank you.

Great Idea. Do you need any resistor on the transistor or IR LED?

Well, normally you would use a 100 ohm resistor between D7 and the transistor and about 470 ohm between the IR LED and the transistor to protect against drawing to much current. However a pulse is very short and the duty cycle is at 50%. so i would not worry to much about it. I had this hardware in a previous setup (under HomeSeer that is) running for years.

1 Like

If it works, it works.

Used this ESPHome code (THANK YOU VERY MUCH!!) with the following inexpensive ($3.50 USD) ESP8285 device from aliexpress:
Infrared Transceiver ESP8285 Wireless WIFI Transceiver Module Remote Control

Details:

esp8266:
  board: esp8285

remote_receiver:
  pin:
    number: GPIO14
    inverted: true
  dump: all
  
remote_transmitter:
  pin: GPIO4
  carrier_duty_percent: 50%

Unfortunately, while receiving and decoding is working perfectly, using the ESPHome service to transmit is consistently causing the device to restart. Time to start digging, I guess.
Well, DUH. Seems when it asks for protocol, it doesn’t want LG. It wants transmit_lg :roll_eyes:

It appears the particular sensor I’m using has a flaky transmitter connection somewhere. Sometimes it works, others it does not. Fortunately, I bought a total of three of these, specifically in case there were any problems with one. If it’s not a bad solder connection, it’s probably a faulty IR LED.

Re-soldered the connections, and it’s not that. What it appears is the transmitter range/strength/intensity is just too feeble to reach across several feet of clear space. :frowning: Well, what else could we expect for $3.50 USD? Oh well…

1 Like

Wow! Wonderful code! Thank you very much.
I has some AEHA and MIDEA remotes to control through ESP IR remote transmitter.
I tried to modify your code but my lambda function knowledge is near to zero :pensive:.
There are any possibility to include this two protocols?
The problem seems to be that booth protocols need commands “lists” instead “string”.
https://esphome.io/components/remote_transmitter.html#remote-transmitter-transmit-aeha-action

Thank You again.

How to record codes from unknown remotes?

Sorry for the late replay but I was away for several months.
But sorry again, i can’t help you with that. Just look at all the other protocols and try to figure it out by just trying.

1 Like

How to record unknown remotes?
Open the log of the blaster in EspHome. Point your remote at the attached IR receiver and press the button you want to record. The details will come up in the log after each button press.

1 Like

Thanks.
I tried that, but there are too many codes to record. To use as climate component, I have to record each temperature in every mode (heat, cool, dry and fan, with every fan speed and oscilation, adding turbo, sleep and quiet modes!), because my remote gives all the info while pressing the button.
I solved this using tasmota instead of esphome, with the tasmota daikin protocol it just works!

@FredTheFrog can you share the whole of your modified configuration for this project? Thank you.

My config is strictly using the LG protocol for televisions.
If you still want it, please let me know.

Thank you for the prompt reply. I am looking for LG aircons. Yes please share regardless as I’m new in ESPHome DIY IR blaster stuff and trying to learn everything.

# IR Blaster
# by AA van Zoelen
#
# Version: 1.1
# Date   : 23 Aug 2022
#
# Description
# ===========
# Protocol : String in UPPERCASE
# Code     : Some protocols name this also data, address, wand_id, rc_code1
# Command  : Some protocols name this also nbits, magnitude, rc_code2
# Repeat   : Optionally set the code to be repeated a number of times. Otherwise use 1.
#
# Implemented protocols
# - LG
#   o POWER       data=0x20DF10EF, nbits=32
#   o MUTE        data=0x20DF906F, nbits=32
#   o HOME        data=0x20DF3EC1, nbits=32
#   o VOL+        data=0x20DF40BF, nbits=32
#   o VOL-        data=0x20DFC03F, nbits=32
#   o CH+         data=0x20DF00FF, nbits=32
#   o CH-         data=0x20DF807F, nbits=32
#   o 0           data=0x20DF08F7, nbits=32
#   o 1           data=0x20DF8877, nbits=32
#   o 2           data=0x20DF48B7, nbits=32
#   o 3           data=0x20DFC837, nbits=32
#   o 4           data=0x20DF28D7, nbits=32
#   o 5           data=0x20DFA857, nbits=32
#   o 6           data=0x20DF6897, nbits=32
#   o 7           data=0x20DFE817, nbits=32
#   o 8           data=0x20DF18E7, nbits=32
#   o 9           data=0x20DF9867, nbits=32
#   o ARROW UP    data=0x20DF02FD, nbits=32
#   o ARROW DOWN  data=0x20DF827D, nbits=32
#   o ARROW LEFT  data=0x20DFE01F, nbits=32
#   o ARROW RIGHT data=0x20DF609F, nbits=32
#   o OK          data=0x20DF22DD, nbits=32
#   o SLEEP       data=0x20DF708F, nbits=32
#   o INPUT       data=0x20DFD02F, nbits=32
#   o INFO        data=0x20DF55AA, nbits=32
#
# energy->20DFA956
# av. mode->20DF0CF3
# input->20DFD02F
# tv/rad->20DF0FF0
# list->20DFCA35
# quick view->20DF58A7
# guide->20DFD52A
# home->20DFC23D
# fav->20DF7887
# ratio->20DF9E61
# back->20DF14EB
# info->20DF55AA
# exit->20DFDA25
# red button->20DF4EB1
# green button->20DF8E71
# yellow button->20DFC639
# blue button ->20DF8679
# text->20DF04FB
# t.opot->20DF847B
# subtitle->20DF9C63
# stop->20DF8D72
# play->20DF0DF2
# pause->20DF5DA2
# rew->20DFF10E
# forward->20DF718E
# ad->20DF8976
#
# POWER OFF->20DFA35C
# POWER ON->20DF23DC
#
# INPUT Antenna->20DF6B94
# INPUT Component 1->20DFFD02
# INPUT AV->20DF5AA5
# INPUT HDMI1->20DF738C
# INPUT HDMI2->20DF33CC
# INPUT HDMI3->20DF9768
#
# Netflix->20DF6A95
# Amazon->20DF3AC5
#
#
# Details
# -------
# MCU used is an 8285
#
# Minimal components
# IR receiver such as TSOP38238 - IR receiver 
# NPN transistor (e.g. BC549C, 2n2222)
# IR LED
#
esphome:
  name: ir-blaster-downstairs

esp8266:
  board: esp8285

# Enable logging
logger:
  level: INFO

ota:
  password: "redacted"

wifi:
  ssid:     !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none
  fast_connect: true
  reboot_timeout:  3min
  use_address:     ir-blaster-downstairs.local
  manual_ip:
    static_ip: 192.168.1.59
    gateway: 192.168.1.1
    subnet: 255.255.255.0  

button:
  - platform: template
    name: "Power LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF10EF
          nbits: 32

  - platform: template
    name: "Power OFF LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DFA35C
          nbits: 32
          
  - platform: template
    name: "Power ON LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF23DC
          nbits: 32

  - platform: template
    name: "UP Arrow LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF02FD
          nbits: 32
          
  - platform: template
    name: "DOWN Arrow LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF827D
          nbits: 32
          
  - platform: template
    name: "LEFT Arrow LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DFE01F
          nbits: 32
          
  - platform: template
    name: "RIGHT Arrow LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF609F
          nbits: 32
  
  - platform: template
    name: "Input LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DFD02F
          nbits: 32

# INPUT Antenna->20DF6B94
# INPUT HDMI1->20DF738C
# INPUT HDMI2->20DF33CC
# INPUT HDMI3->20DF9768
          
  - platform: template
    name: "Mute LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF906F
          nbits: 32

  - platform: template
    name: "Home LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF3EC1
          nbits: 32

  - platform: template
    name: "Vol Up LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF40BF
          nbits: 32

  - platform: template
    name: "Vol Down LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DFC03F
          nbits: 32

  - platform: template
    name: "Chan Up LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF00FF
          nbits: 32

  - platform: template
    name: "Chan Down LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF807F
          nbits: 32

  - platform: template
    name: "Arrow Up LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF02FD
          nbits: 32

  - platform: template
    name: "Arrow Down LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF827D
          nbits: 32

  - platform: template
    name: "Arrow Left LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DFE01F
          nbits: 32

  - platform: template
    name: "Arrow Right LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF609F
          nbits: 32

  - platform: template
    name: "Okay LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF22DD
          nbits: 32

  - platform: template
    name: "0 Zero LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF08F7
          nbits: 32

  - platform: template
    name: "1 One LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF8877
          nbits: 32

  - platform: template
    name: "2 Two LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF48B7
          nbits: 32

  - platform: template
    name: "3 Three LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DFC837
          nbits: 32

  - platform: template
    name: "4 Four LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF28D7
          nbits: 32

  - platform: template
    name: "5 Five LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DFA857
          nbits: 32

  - platform: template
    name: "6 Six LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF6897
          nbits: 32

  - platform: template
    name: "7 Seven LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DFE817
          nbits: 32

  - platform: template
    name: "8 Eight LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF18E7
          nbits: 32

  - platform: template
    name: "9 Nine LG"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x20DF9867
          nbits: 32

# Enable Home Assistant API
api:
  encryption:
    key: !secret encryption_key

  services:
    - service: send_ir
      variables:
        protocol: string
        code: string
        command: string
        repeats: string
      then:
        - lambda: 'ESP_LOGD("main", "Protocol: %s - Code: %s - Command: %s",  protocol.c_str(), code.c_str(), command.c_str() ); '
        - if:
            condition: # --=[ *** LG *** ]=--
              lambda: 'return protocol == "LG";'
            then:
              - logger.log: "Sending LG data...!"
              - remote_transmitter.transmit_lg:
                  data: !lambda |-
                       int i;
                       sscanf(code.c_str(), "%x", &i );
                       return i;
                  nbits: !lambda |-
                        return std::stoi( command );

#remote_receiver:
#  pin:
#    number: GPIO14
#    inverted: true
#  dump: lg

remote_transmitter:
  pin: GPIO4
  carrier_duty_percent: 50%
1 Like

@FredTheFrog Thank you so much.

has details and a few codes for your LG air conditioner

1 Like

@FredTheFrog Wow! thank you for directing me where to search and thank you for your time.