Multi-device IR Controller

I recently got a dinosaur night light for my kids, that is controlled by an IR remote. Also I have a floor fan (Honeywell/Kaz hy254tgt “Quietset”) that is controlled by IR that I keep in their room as it gets hotter in the summer.

My goal was is to be able to modify the night light settings (color/brightness/on/off) as well as fan speed.

I picked up one of the IR shields for a D1 mini, and came up with the following for ESPHome. I create a representation of the night light and the fan. As long as they’re not controlled by real remotes or physical buttons they work pretty well. I thought i’d share in case this turns out to be helpful to anyone else with similar goals!

I also was going to put a DHT on to better decide on fan speeds but i’m finding that either wifi interference or heat on the D1 mini is making it unreliable. Any advice there is welcome!

esphome:
  name: kids_ir
  platform: ESP8266
  board: d1_mini

wifi:
  ssid: !secret wifi_front_ssid
  password: !secret wifi_front_password
  fast_connect: on

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Kids IR Fallback Hotspot"
    password: !secret fallbck_hotspot

captive_portal:

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

globals:
  - id: previous_dino_state
    type: float
  - id: previous_fan_state
    type: float
  - id: previous_fan_osc_state
    type: float

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

fan:
  - platform: speed
    name: "Quietset fan"
    output: fan_power_output
    oscillation_output: fan_osc_output
    speed:
      low: 0.25
      medium: 0.5
      high: 1.0
light:
  - platform: monochromatic
    name: "Dinosaur Night Light"
    default_transition_length: 0s
    output: dino_output
    effects:
      - lambda:
          name: Green
          update_interval: 30s
          lambda: id(green).execute();
      - lambda:
          name: Red
          update_interval: 30s
          lambda: id(red).execute();
      - lambda:
          name: Blue
          update_interval: 30s
          lambda: id(blue).execute();
      - lambda:
          name: White
          update_interval: 30s
          lambda: id(white).execute();
      - lambda:
          name: Flash
          update_interval: 30s
          lambda: id(flash_pattern).execute();
      - lambda:
          name: Fade
          update_interval: 30s
          lambda: id(fade_pattern).execute();
      - lambda:
          name: Strobe
          update_interval: 30s
          lambda: id(strobe_pattern).execute();
      - lambda:
          name: Smooth
          update_interval: 30s
          lambda: id(smooth_pattern).execute();
output:
  - platform: template
    type: float
    id: dino_output
    write_action:
      - lambda: |-
          ESP_LOGD("brightness", "previous state: %f, new state %f", id(previous_dino_state), state);
          /* turning off */
          if (state == 0) {
            id(turn_off_dino).execute();
          } else {
            /* turn on */
            if (id(previous_dino_state) == 0) {
              id(turn_on_dino).execute();
            }
            /* adjust brightness - 5 increments*/
            if (id(previous_dino_state) < state) {
                for (float i=id(previous_dino_state); i <= state; i+=.15) {
                  delay(300);
                  id(brightness_up).execute();
                }
            } else {
                for (float i=id(previous_dino_state); i >= state; i-=.15) {
                  delay(300);
                  id(brightness_down).execute();
                }
            }
          }
          id(previous_dino_state) = state;
  - platform: template
    type: float
    id: fan_power_output
    write_action:
      - lambda: |-
          ESP_LOGD("fan", "previous state: %f, new state %f", id(previous_fan_state), state);
          /* turning off */
          if (state == 0) {
            id(toggle_fan_power).execute();
          } else {
            /* turn on */
            if (id(previous_fan_state) == 0) {
              id(toggle_fan_power).execute();
            }
            /* adjust speed - need to map 5 increments to 3*/
            if (id(previous_fan_state) != state) {
              int steps = 0;
              float old_state = id(previous_fan_state);
              /* H->L */
              if ((old_state > 0.75) && (state == 0.25)) {
                steps = 1;
              /* 0->M, L->M, M->H */
              } else if (((old_state < 0.25) && (state == 0.50)) ||
                         ((old_state == 0.25) && (state == 0.50)) ||
                         ((old_state == 0.50) && (state == 1.00))) {
                steps = 2;
              /* M->L, H->M */
              } else if (((old_state > 0.75) && (state == 0.50)) ||
                         ((old_state == 0.5) && (state == 0.25))) {
                steps = 3;
              /* 0->H, L->H */
              } else if (((old_state < 0.25) && (state == 1.0)) ||
                         ((old_state == 0.25) && (state == 1.0))) {
                steps = 4;
              }
              ESP_LOGD("fan", "%d increment steps", steps);
              for (int i=0; i < steps; i++) {
                delay(300);
                id(increment_fan_speed).execute();
              }
            }
          }
          id(previous_fan_state) = state;
  - platform: template
    type: float
    id: fan_osc_output
    write_action:
      - lambda: |-
          ESP_LOGD("fan osc", "previous state: %f, new state %f", id(previous_fan_osc_state), state);
          if (id(previous_fan_osc_state) != state) {
            id(toggle_fan_oscillate).execute();
            id(previous_fan_osc_state) = state;
          }

script:
  - id: brightness_up
    then:
      - remote_transmitter.transmit_lg:
          data: 0x00FFA05F
          nbits: 32
  - id: brightness_down
    then:
      - remote_transmitter.transmit_lg:
          data: 0x00FF20DF
          nbits: 32
  - id: turn_on_dino
    then:
    #turn on light
    - remote_transmitter.transmit_lg:
        data: 0x00FFE01F
        nbits: 32
  - id: turn_off_dino
    then:
      #turn off light
      - remote_transmitter.transmit_lg:
          data: 0x00FF609F
          nbits: 32
  - id: smooth_pattern
    then:
    #set smooth pattern
      - remote_transmitter.transmit_lg:
          data: 0x00FFC837
          nbits: 32
  - id: fade_pattern
    then:
    #set fade pattern
      - remote_transmitter.transmit_lg:
          data: 0x00FF609F
          nbits: 32
  - id: strobe_pattern
    then:
    #set strobe pattern
      - remote_transmitter.transmit_lg:
          data: 0x00FFD827
          nbits: 32
  - id: flash_pattern
    then:
    #set Flash pattern
      - remote_transmitter.transmit_lg:
          data: 0x00FFE817
          nbits: 32
  - id: red
    then:
    #set red pattern
      - remote_transmitter.transmit_lg:
          data: 0x00FF906F
          nbits: 32
  - id: green
    then:
    #set green pattern
      - remote_transmitter.transmit_lg:
          data: 0x00FF10EF
          nbits: 32
  - id: blue
    then:
    #set blue pattern
      - remote_transmitter.transmit_lg:
          data: 0x00FF50AF
          nbits: 32
  - id: white
    then:
    #set white pattern
      - remote_transmitter.transmit_lg:
          data: 0x00FFD02F
          nbits: 32
  - id: toggle_fan_power
    then:
    #turn on or off the fan
      - remote_transmitter.transmit_raw:
          code: [+1259,-402,+1259,-402,+402,-1259,+1259,-402,+1259,-402,+402,-1259,+402,-1259,+402,-1259,+402,-1259,+1259,-402,+402,-1259,+402,-8087]
          carrier_frequency: 37344
  - id: increment_fan_speed
    then:
    #increment the fan's speed (starts at 1 goes to 5 and then wraps)
      - remote_transmitter.transmit_raw:
          code: [+1259,-402,+1259,-402,+402,-1259,+1259,-402,+1259,-402,+1259,-402,+402,-1259,+402,-1259,+402,-1259,+1259,-402,+1259,-402,+402,-8087]
          carrier_frequency: 37344
  - id: toggle_fan_oscillate
    then:
    #toggle the oscillation mode of the fan
      - remote_transmitter.transmit_raw:
          code: [+1259,-402,+1259,-402,+402,-1259,+1259,-402,+1259,-402,+1259,-402,+402,-1259,+402,-1259,+402,-1259,+402,-1259,+1259,-402,+1259,-7230]
          carrier_frequency: 37344

#Disable until needing to program more
#remote_receiver:
#  pin:
#    number: D4
#    #D1 mini IR shield needs to be flipped
#    inverted: True
#  dump: all

switch:
  #this is here since fan.oscillate() service from HA seems to not be working
  - platform: template
    name: QuietSet Fan Oscillate
    turn_on_action:
      - remote_transmitter.transmit_raw:
          code: [+1259,-402,+1259,-402,+402,-1259,+1259,-402,+1259,-402,+1259,-402,+402,-1259,+402,-1259,+402,-1259,+402,-1259,+1259,-402,+1259,-7230]
          carrier_frequency: 37344

#unused color patterns
#switch:
#  - platform: template
#    name: Kids Night Light Red-Orange
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FFB04F
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Orange
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FFA857
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Orange-Yellow
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF9867
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Yellow
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF8877
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Light green
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF30CF
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Aquamarine
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF28D7
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Aquamarine Green
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF18E7
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Dark Green
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF08F7
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Light blue
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF708F
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Purple
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF6897
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Mauve
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF58A7
#          nbits: 32
#  - platform: template
#    name: Kids Night Light Pink
#    turn_on_action:
#      - remote_transmitter.transmit_lg:
#          data: 0x00FF48B7
#          nbits: 32

Looks nice!
I’ve implemented my sv receiver also via ir, but used template switches for everything. Power is normal, vol up/down has only a pseudo turn_off action and since I use only 2 inputs, I turn the other input switch when I turn the other one off.
For the dht add a bit of distance between esp and dht, so the heat and wifi signals can’t influence the dht as much as now. I got a bmp280 and positioned it outside of the case so it can’t get influenced much and it’s temperature readings are more accurate