IR remote to template light with all controls

I just managed to create a template light that sends the commands via IR blaster.
It can do all colors (from a 24 key IR remote, but it can easily be expanded for larger remotes), brightness and effects.

image

The only limitation is that you can’t get saturation, so my way of solving that is if you click where the saturation is more than 70% then it gets converted to 100%, less than 70 is 0% (white light).

Because the 24 key remotes do not have all colors then clicking on the wheel where there is no corresponding button on the remote “rounds” the click to the closest color.

The IR blaster is a ready made ESP device sold on aliexpress.
I use ESP-Home to control the blaster.

ESP-Home yaml:

remote_receiver:
  pin:
    number: GPIO14
    inverted: true
    mode:
      input: true
      pullup: true
  dump: lg
  
  
remote_transmitter:
  pin: GPIO4
  carrier_duty_percent: 50%

button:
  - platform: template
    name: "David TV backlight on"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFE01F
          nbits: 32       

  - platform: template
    name: "David TV backlight off"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF609F
          nbits: 32       

  - platform: template
    name: "David TV backlight flash"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFF00F
          nbits: 32       

  - platform: template
    name: "David TV backlight strobe"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFE817
          nbits: 32       

  - platform: template
    name: "David TV backlight fade"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFD827
          nbits: 32       

  - platform: template
    name: "David TV backlight smooth"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFC837
          nbits: 32      
          
  - platform: template
    name: "David TV backlight none"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFD02F  # makes it white
          nbits: 32      

  - platform: template
    name: "David TV backlight dim up"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFA05F
          nbits: 32      

  - platform: template
    name: "David TV backlight dim down"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF20DF
          nbits: 32      
          
######################################################
######## the number represents the hue value ################
######################################################
# column 1

  - platform: template
    name: "David TV backlight 0"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF906F
          nbits: 32      

  - platform: template
    name: "David TV backlight 15"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFB04F
          nbits: 32      
          
  - platform: template
    name: "David TV backlight 30"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFA857
          nbits: 32      

  - platform: template
    name: "David TV backlight 45"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF9867
          nbits: 32      

  - platform: template
    name: "David TV backlight 60"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF8877
          nbits: 32      

######################################################
# column 2
          
  - platform: template
    name: "David TV backlight 120"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF10EF
          nbits: 32      

  - platform: template
    name: "David TV backlight 135"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF30CF
          nbits: 32      

  - platform: template
    name: "David TV backlight 150"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF28D7
          nbits: 32      

  - platform: template
    name: "David TV backlight 165"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF18E7
          nbits: 32      

  - platform: template
    name: "David TV backlight 180"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF08F7
          nbits: 32      

######################################################
# column 3          

  - platform: template
    name: "David TV backlight 240"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF50AF
          nbits: 32      

  - platform: template
    name: "David TV backlight 255"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF708F
          nbits: 32      

  - platform: template
    name: "David TV backlight 270"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF6897
          nbits: 32      

  - platform: template
    name: "David TV backlight 285"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF58A7
          nbits: 32      

  - platform: template
    name: "David TV backlight 300"
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FF48B7
          nbits: 32      

######################################################
          
  - platform: template
    name: "David TV backlight W" # white
    on_press:
      - remote_transmitter.transmit_lg:
          data: 0x00FFD02F
          nbits: 32          
      

We need two helpers, one input_number to hold the brightness of the light.
The light we have has 5 brightness steps, because of that I make the input_number 6 steps ( 0-5).
This because the template light has 0 brightness, and I use this to turn off the light.

We also need a input_select with the effects the light has.

Light template (configuration.yaml):

light:
  - platform: template
    lights:
      david_tv_backlight:
        friendly_name: "David TV backlight"
        level_template: "{{ (states('input_number.david_tv_backlight_brightness')|int) * (255/6) }}" 
        turn_on:
          service: button.press
          data: {}
          target:
            entity_id: button.david_tv_backlight_on
            
            
        turn_off:
          - service: button.press
            data: {}
            target:
              entity_id: button.david_tv_backlight_off
          - service: input_number.set_value
            data:
              value: 0
              entity_id: input_number.david_tv_backlight_brightness
              
              
        effect_list_template: "{{ state_attr('input_select.david_tv_backlight_effects', 'options') }}"
        effect_template: "{{ states('input_select.david_tv_backlight_effects') }}"

        # brightness is optimistic, so it remembers the brightness since last use, and assumes 
        # the brightness is the same. It will not work properly if you use the real remote.
        set_level:
          - if:
              - condition: template
                value_template: "{{ (brightness / (255/6)) | round  == 0 }}"
            then:
              - service: button.press
                data: {}
                target:
                  entity_id: button.david_tv_backlight_off
              - service: light.turn_off
                data: {}
                target:
                  entity_id: light.david_tv_backlight
            else:
              - repeat:
                  count: "{{ (((brightness / (255/6)) | round) - states('input_number.david_tv_backlight_brightness')| int) | abs }}"
                  sequence:
                    - if:
                        - condition: template
                          value_template: "{{ ((brightness / (255/6)) | round) > states('input_number.david_tv_backlight_brightness')| int }}"
                      then:
                        - service: button.press
                          data: {}
                          target:
                            entity_id: button.david_tv_backlight_dim_up
                      else:
                        - service: button.press
                          data: {}
                          target:
                            entity_id: button.david_tv_backlight_dim_down    
                    - delay:
                        milliseconds: 500
              - service: input_number.set_value
                data:
                  value: "{{ (brightness / (255/6)) | round  }}"
                  entity_id: input_number.david_tv_backlight_brightness
            
        set_color:
          - service: button.press
            data: {}
            target: 
              entity_id: >-
                  {% if( s > 70 ) %}
                    
                    {% if( h > 60 and h <= 90) %}
                      {% set h = 60 %}
                    {% elif( h > 90 and h < 120) %}
                      {% set h = 120 %}
                    {% elif( h > 180 and h <= 210) %}
                      {% set h = 180 %}
                    {% elif( h > 210 and h < 240) %}
                      {% set h = 240 %}
                    {% elif( h > 300 and h <= 330) %}
                      {% set h = 300 %}
                    {% elif( h > 330 and h < 360) %}
                      {% set h = 0 %}
                    {% endif %}
                    
                    button.david_tv_backlight_{{ (((h / 15) | int) * 15) % 360 }}    
                  {% else %}
                    button.david_tv_backlight_w
                  {% endif %}
                
                
          # this is to round the template light to the same color as we sent to the light.
          # by some reson it doesn't always work that is why I added it twice, that gives it a bit better success rate       
          - service: light.turn_on
            data:
              hs_color: >- 
                  {% if( s > 70 ) %}
                    {% set s = 100 %}
                    
                    {% if( h > 60 and h <= 90) %}
                      {% set h = 60 %}
                    {% elif( h > 90 and h < 120) %}
                      {% set h = 120 %}
                    {% elif( h > 180 and h <= 210) %}
                      {% set h = 180 %}
                    {% elif( h > 210 and h < 240) %}
                      {% set h = 240 %}
                    {% elif( h > 300 and h <= 330) %}
                      {% set h = 300 %}
                    {% elif( h > 330 and h < 360) %}
                      {% set h = 0 %}
                    {% endif %}
                    
                  {% else %}
                    {% set s = 0 %}
                  {% endif %}
                  [{{ (((h / 15) | int) * 15) % 360 }}, {{ s }}]
            target:
              entity_id: light.david_tv_backlight
          - service: light.turn_on
            data:
              hs_color: >- 
                  {% if( s > 70 ) %}
                    {% set s = 100 %}
                    
                    {% if( h > 60 and h <= 90) %}
                      {% set h = 60 %}
                    {% elif( h > 90 and h < 120) %}
                      {% set h = 120 %}
                    {% elif( h > 180 and h <= 210) %}
                      {% set h = 180 %}
                    {% elif( h > 210 and h < 240) %}
                      {% set h = 240 %}
                    {% elif( h > 300 and h <= 330) %}
                      {% set h = 300 %}
                    {% elif( h > 330 and h < 360) %}
                      {% set h = 0 %}
                    {% endif %}
                    
                  {% else %}
                    {% set s = 0 %}
                  {% endif %}
                  [{{ (((h / 15) | int) * 15) % 360 }}, {{ s }}]
            target:
              entity_id: light.david_tv_backlight
                
        set_effect:
          - service: button.press
            data: {}
            target:
              entity_id: button.david_tv_backlight_{{ effect }}

This now gives me a GUI control of an IR controlled light

10 Likes

Hello. Nice template, I am building my own based on yours using scenes from a Tuya IR controler.
But I found that when the light turns on it doesn’t rebember que last brightness status, always returns 0.
I think I can get past that using another input_number that doesn’t get set to 0 on turn_off, but I wanted to make sure that I’m doing everything correctly and that is the present behaviour.

Summing, how does the light get the old brightness value when it is turned on?

Thanks for your work!

I believe the brightness and other things being saved past off depends on the manufacturer.
Some does, some doesn’t.

I’m not sure you can create this here.
You could try using the turn off sequence to set a copy of the brightness before turned of.
But I’m not sure how you are going to do when you turn on, perhaps a loop that loops and sends the signal to dim down, but that will be quite visible and probably annoying.

Maybe I didn’t explain the best way; it is not the light, but the template that sets the brighness to 0 on the turn_off command:

        turn_off:
          - service: button.press
            data: {}
            target:
              entity_id: button.david_tv_backlight_off
          - service: input_number.set_value
            data:
              value: 0
              entity_id: input_number.david_tv_backlight_brightness

On turn_on service there is no way to know the value before. Any suggestions?
I’m gonna try to remove that last piece of code, that sets input_number value to 0, and see how it behaves when turned off (as sson as I get home).
The color approach is very ingenious, congrats! :slight_smile:

EDIT: the removal of the service input_number.set_value on the turn_off command solved it form me; I understand now what you where trying to say: my bulb remembers the last brightness before turning off, so I don’t need that service call; the only downside is that the brightness is set to a positive value even if the lamp is off. But does not affect the behaviour of the template light.

My remote was slightly different and appeared to have different hue values available. I kept adjusting the hues based on what I visually saw from the sign, so I just put the hue values in a “dropdown” helper. Also my LED controller remembered the previous brightness when it was powered back on, so using zero to indicate powered off didn’t work very well. My LEDs also only had 5 brightness levels so I set max=5 (0 is unused, then 1, 2, 3, 4, 5 are the brightness levels).

Also I used IrScrutinizer to check out my remote’s codes using a Linux machine and it said it is transmitting NEC remote codes. The remote only emits NEC address zero. The keys map to command 0 and onward starting at the upper left of the remote, incrementing across each column, and then starting at 3 on the left side of the second row. Image attached with the first two rows numbered for illustrative purposes.

Here is the end of my ESPhome configuration. I have an M5 Atom Lite which has a built-in IR transmitter on pin 12:

remote_transmitter:
  pin: GPIO12
  # Infrared remotes use a 50% carrier signal
  carrier_duty_percent: 50%

button:
  - platform: template
    name: "LED Bright Up"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xFF00
          command_repeats: 1

  - platform: template
    name: "LED Bright Down"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xFE01
          command_repeats: 1

  - platform: template
    name: "LED Power On"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xFD02
          command_repeats: 1

  - platform: template
    name: "LED Power Off"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xFC03
          command_repeats: 1
# Row 2
  - platform: template
    name: "LED Color 30"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xFB04
          command_repeats: 1

  - platform: template
    name: "LED Color 120"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xFA05
          command_repeats: 1

  - platform: template
    name: "LED Color 250"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF906
          command_repeats: 1

  - platform: template
    name: "LED Color W"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF807
          command_repeats: 1
#row 3
  - platform: template
    name: "LED Color 45"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF708
          command_repeats: 1

  - platform: template
    name: "LED Color 135"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF609
          command_repeats: 1

  - platform: template
    name: "LED Color 300"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF50A
          command_repeats: 1

  - platform: template
    name: "LED Mode Flash"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF40B
          command_repeats: 1
# row 4
  - platform: template
    name: "LED Color 60"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF30C
          command_repeats: 1

  - platform: template
    name: "LED Color 150"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF20D
          command_repeats: 1

  - platform: template
    name: "LED Color 320"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF10E
          command_repeats: 1

  - platform: template
    name: "LED Mode Strobe"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xF00F
          command_repeats: 1
# row 5
  - platform: template
    name: "LED Color 90"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xEF10
          command_repeats: 1

  - platform: template
    name: "LED Color 200"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xEE11
          command_repeats: 1

  - platform: template
    name: "LED Color 290"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xED12
          command_repeats: 1

  - platform: template
    name: "LED Mode Fading"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xEC13
          command_repeats: 1
# row 6
  - platform: template
    name: "LED Color 105"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xEB14
          command_repeats: 1

  - platform: template
    name: "LED Color 220"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xEA15
          command_repeats: 1

  - platform: template
    name: "LED Color 300"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xE916
          command_repeats: 1

  - platform: template
    name: "LED Mode Smooth"
    on_press:
      - remote_transmitter.transmit_nec:
          address: 0xFF00
          command: 0xE817
          command_repeats: 1

I ended up creating these Helpers in Home Assistant (SettingsDevices & ServicesHelpers):

  • foobar_power - [Boolean] Power state of sign
  • foobar_brightness - [Number] Brightness level of sign (set max: 5)
  • foobar_colors - [Dropdown] Possible hue values; first entry should be “w” and then the rest are the numbers from the ESPhome config (e.g., “foobar_led_color_NNN” where NNN is the color)
  • foobar_effects - [Dropdown] Possible effects; should match the entries in ESPhome (e.g., “foobar_led_mode_XYZ” where XYZ is the mode)

Here is the code I put into light: part of my Home Assistant configuration:

- platform: template
  lights:
    foobar_sign:
      friendly_name: "Foobar Sign"
      value_template: "{{ states('input_boolean.foobar_power') }}"
      level_template: >-
        {% if is_state('input_boolean.foobar_power', 'off') %}
          0
        {% else %}
          {{ states('input_number.foobar_brightness') | int * (255 / state_attr('input_number.foobar_brightness', 'max') | int) }}
        {% endif %}
      hs_template: >-
        {% if states('input_select.foobar_colors') == state_attr('input_select.foobar_colors', 'options')[0] %}
          (0, 0)
        {% else %}
          ({{ states('input_select.foobar_colors') | int }}, 100)
        {% endif %}
      effect_list_template: "{{ state_attr('input_select.foobar_mode', 'options') }}"
      effect_template: "{{ states('input_select.foobar_mode') }}"

      turn_on:
        - service: button.press
          target:
            entity_id: button.foobar_led_power_on
        - service: input_boolean.turn_on
          target:
            entity_id: input_boolean.foobar_power

      turn_off:
        - service: button.press
          target:
            entity_id: button.foobar_led_power_off
        - service: input_boolean.turn_off
          target:
            entity_id: input_boolean.foobar_power

      set_level:
        - variables:
            scaled: "{{ (brightness / (255 / state_attr('input_number.foobar_brightness', 'max'))) | round | int }}"
            diff: "{{ scaled | int - states('input_number.foobar_brightness') | int }}"
            direction: "{{ 'down' if diff | int < 0 else 'up' }}"
        - if:
            - condition: template
              value_template: "{{ scaled | int == 0 }}"
          then:
            - service: light.turn_off
              target:
                entity_id: light.foobar_sign
          else:
            - service: light.turn_on
              target:
                entity_id: light.foobar_sign
            - repeat:
                count: "{{ diff | int | abs }}"
                sequence:
                  - service: button.press
                    target:
                      entity_id: "button.foobar_led_bright_{{ direction }}"
                  - delay:
                      milliseconds: 500
            - service: input_number.set_value
              data:
                value: "{{ scaled }}"
                entity_id: input_number.foobar_brightness

      set_effect:
        - service: button.press
          target:
            entity_id: >-
              {% if effect == 'normal' %}
                button.foobar_led_color_w
              {% else %}
                button.foobar_led_mode_{{ effect }}
              {% endif %}

      set_hs:
        - variables:
            color: >-
              {%- if s < 60 %}
                {{ state_attr('input_select.foobar_colors', 'options')[0] }}
              {% else %}
                {%- set o = namespace(val = {"dist": 360, "v": 0}) -%}
                {%- for x in state_attr('input_select.foobar_colors', 'options')[1:] | map('int') -%}
                  {%- set x_dist = [(360 + h - x) | abs, (h - x) | abs] | min -%}
                  {%- if x_dist < o.val.dist -%}
                    {%- set o.val = {"dist": x_dist, "v": x} -%}
                  {%- endif -%}
                {%- endfor -%}
                {{ o.val.v }}
              {% endif %}
        - service: input_select.select_option
          target:
            entity_id: input_select.foobar_colors
          data_template:
            option: "{{ color }}"
        - service: button.press
          target:
            entity_id: "button.foobar_led_color_{{ color }}"