How to: Add IR tranmitter to enhance your audio video experince

Background
In this post I provide details for building and integrating IR transmitters to control AV components. A raspberry pi is used at each point where I want to transmit IR signals. The post tells you how to enable and wire IR transmission and receiving on a RPI. It also includes examples of how I send IR scancodes to both a Samsung TV and a pioneer AV receiver.

In a previous post I document setting up a whole house audio system. In reality audio is only half of a typical home entertainment system. Video is also important. If you have all new high end components you’ll probable be able to use network base command and control for all of your components. However, if you’re like most people, you have a mixture of old and new components. Some components will have network controls, others will have IR controls, while still others may have no smart controls at all. In another post I documented steps I took to smarten up a very old dumb stereo amp.

My smart network tv is connected via wifi, how do I turn it on

From what I’ve seen Wake On Lan seems to be one of the primary methods utilized by HA integrations to turn on a smart TV connected to the network. This works fine if the TV is connected via Ethernet, however most people connect their TV to their local network via WiFi. Wake On Lan doesn’t work over WiFi. I have a Samsung TV that I can fully control via an HA integration, with one sad exception. I can’t turn it on using the HA integration because the integration uses Wake On Lan. In the post I mentioned above that documents building a full house audio system I document placing a squeezelite media player that runs on a Raspberry PI 3 (RPI3) next to any device that I want to have provide audio output. One way to address the turn on problem is to turn that squeezelite RPI3 into a Ethernet to WiFi bridge and then connect the tv via Ethernet to the RPI3 Ethernet port. This mainly works unless you want to turn the tv back on shortly after turning it off. When you turn the TV off it’s not really off, its in some kind of snooze state. As such you can not wake it via Wake On Lan. A better way that always works is to transmit the remote control IR power on signal. So by adding IR transmission capabilities to my squeezelite RPI3 I can use it to provide network controllable IR transmission capabilities.

Enabling IR receive and transmission capabilities on a RPI

While you only need the IR transmission capability to control your IR enabled components, you need some way to figure out what signal to transmit related to the appropriate button on the remote control you’ll be emulating. As such you really need both transmit and receive capabilities on at least one device.

The Raspberry PI operating system makes it pretty easy to enable IR transmit and Receive capabilities. In the /boot/config.txt file you should fine these two lines commented out via # at the start of the line. You remove the # character to enable the lines so they look like this:

dtoverlay=gpio-ir,gpio_pin=18
dtoverlay=gpio-ir-tx,gpio_pin=17

You need to reboot the RPI for these to take affect. You then need to install two packages that will be used to first discover signals from your remote control and secondly transmit these signals:

apt install ir-keytable
apt install v4l-utils

You can get IR transmitters and receivers here. If you look on the internet for instructions on connecting these to the GPIO pins they always include at least one resisters for both the transmitter and receiver. They also include a transistor for the transmitter. I’m not an electrical engineer so I could be missing something, but I don’t use the resister because the RPI3 gpio pins have built in resisters. I’ve run a receiver for years with no resister and have had 0 issues. The IR receiver does a great job of capturing the signal from my remote controls. The transistor included in the transmitter circuit is to strengthen the transmitted signal strength. The on line documentation tells you that connecting the transmitter directly to the GPIO pins will not provide enough power. They are correct if you want to transmit the signal more than a few feet. However in my setup I just put the transmitter within a few inches of the receiving device and all is good. Here’s a picture of one device with both the receiver and transmitter connected.

While the receiver is just sticking out of the top of the RPI case, the transmitter is connected via longer wires and is sitting at the bottom left of the picture in front of the FIOS cable tuner to control it.

Wiring in the transmitter and receiver

Here is the RPI pinout picture:

image

I include this because up above we configured the Raspberry PI OS to use GPIO 17 and GPIO 18 for transmit and receive respectively, which aree actually pins 11 and 12.

On the transmitter LED the longer of the two leads is the positive side. This longer lead needs to be connected to gpio 17 (pin 11 ) and then the shorter lead needs to be connected to ground (pin 9).

For the receiver you have three leads. If you look at the receiver as oriented in the picture above, the left lead gets connected to gpio 18 (pin 12 ), the center to ground (pin 6) and the right lead to 5 volt (pin 2).

Capture remote scan codes

There are two methods I’ve used to capture and transmit IR codes. In the easiest method I used ir-keytable to discover scancodes as I push button on the remote I want to emulate. With the IR receiver connected and the software installed you should be able to run the following command to catch an IR signal:

sudo ir-keytable -v -t -p rc-5,rc-5-sz,jvc,sony,nec,sanyo,mce_kbd,rc-6,sharp,xmp -s rc1

If this errors out then change the rc1 at the end of the line to rc0. If the command works it should print out some lines ending with

Protocols changed to rc-5 rc-5-sz jvc sony nec sanyo mce_kbd rc-6 sharp xmp 
Testing events. Please, press CTRL-C to abort.

Pushing buttons on the remote you want to emulate should provide output similar to this

1796817.910060: lirc protocol(nec): scancode = 0xa51c
1796817.910087: event type EV_MSC(0x04): scancode = 0xa51c
1796817.910087: event type EV_SYN(0x00).
1796822.140068: lirc protocol(nec): scancode = 0xa55c
1796822.140099: event type EV_MSC(0x04): scancode = 0xa55c
1796822.140099: event type EV_SYN(0x00).
1796822.230135: lirc protocol(nec): scancode = 0xa5c0
1796822.230162: event type EV_MSC(0x04): scancode = 0xa5c0
1796822.230162: event type EV_SYN(0x00).

You want to document the scancode and it’s associated button as you’ll need them in the transmission script. You also need the protocol reported. The above were codes from an AV surround sound amplifier. The first was the power button. I can then use the following command to transmit this remote key:

sudo ir-ctl -S nec:0xa51c

I mentioned above I needed a way to turn on the Samsung TV. When attempting to record the Samsung remote, the power button was the only button that provide a scan code. Good thing it was the only key I needed to transmit. This is the output provided:

1797459.520081: lirc protocol(necx): scancode = 0x707e6
1797459.520111: event type EV_MSC(0x04): scancode = 0x707e6
1797459.520111: event type EV_SYN(0x00).
1797459.630069: lirc protocol(necx): scancode = 0x707e6
1797459.630094: event type EV_MSC(0x04): scancode = 0x707e6
1797459.630094: event type EV_SYN(0x00).

So I should have been able to play this code with the following:

sudo ir-ctl -S necx:0x707e6

Sadly this didn’t work. Don’t worry yet as all is not lost, there is a second method for making a raw capture of the IR signal. You capture the raw signal information with this command:

ir-ctl -d /dev/lirc1 --receive=samsung_power.key

There is a possibly that your receiver device could be /dev/lirc0, so if get an error message stating lirc1 has no receiving capability then try lirc0. Assuming the above doesn’t error out you push the key you want to record then you use c to stop the ir-ctl command. Looking at the created samsung_power.key file and you’ll see something like the following:

pulse 4509
space 4452
pulse 607
space 1648
pulse 607
space 1649
pulse 607
space 1650
...
...
...
space 519
pulse 607
space 520
pulse 606
timeout 75841

You need to delete the timeout line from the end of this file. Then you can replay this IR signal with the following command:

sudo ir-ctl -d /dev/lirc0 --send=samsung_power.key

This method worked for me with the Samsung TV.

In order to enable your user account to transmit the IR signal without using sudo you can add your user account to the video group. If you’re just using the default pi user then it should already be in the video group.

We now create a script file ir-wake-samsung in your users home directory with the following in it:

#!/bin/bash
ir-ctl -d /dev/lirc0 --send=samsung_power1.key

This is the most basic script that is capable of sending one remote key. You set the permission of this file to 755.

Update HA to sent the samsung remote control power key
Obviously I’m documenting using a Samsung TV, you should be able to do something similar with any TV as long as the OS understand the remote control protocol and produced scancodes.

The first thing you need to do on the HA side is create a shell command that can remotely execute the above script on the remote RPI3. If you don’t already have the HA “Terminal & SSH” add-on installed on your HA box install it from the Add-on Store in the HA GUI. From the Terminal & SSH" add on you can start a terminal window from the “OPEN WEB UI” link. This gives you an shell on your HA box. Get into the base configuration directory via:

cd /root/config

I place all of my HA shell commands in the directory shell_cmds, which you can create via:

mkdir shell_cmds
cd shell_cmds

You can then use either the vi or nano editor to create the shell command “wake_samsung_tv” that will execute the script on the remote pi and transmit the remote control scancode. The file should contain the following:

#!/bin/bash
ssh -i /config/.ssh/id_rsa -o StrictHostKeyChecking=no YOURUSER@RPI_IP_ADDR ./ir-wake-samsung

You update YOURUSER and RPI_IP_ADDR with your user account on the RPI and the IP address for the RPI. The permission of the file should also be 755.

If you haven’t previously made an ssh key pair on your HA box via the Terminal window you do that with the following commands

cd /root/config
mkdir .ssh
cd .ssh
ssh-keygen -t rsa

It will ask you where to save the key, enter

/root/config/.ssh/id_rsa

It’ll ask you for a passphrase, just hit the enter/return key
It’ll ask you for the passphare again, just hit the enter/return key
See that they key pair has been generated:

ls -l 

Push the id_rsa.pub key to your RPI that will be sending remote control commands:

scp id_rsa.pub  YOURUSER@RPI_IP_ADDR:

You update YOURUSER and RPI_IP_ADDR with your user account on the RPI and the IP address for the RPI.

Back on the remote RPI you need to add this key to YOURUSER’s authorized_key file

cd ~/
cat  *.pub  >> .ssh/authorized_keys
chmod 600  .ssh/authoized_keys

Jump back to the HA web terminal and make sure the key pair is working

ssh -i ./id_rsa  -o StricthostKeyChecking=no YOURUSER@RPI_IP_ADDR

Assuming this logs you on to the remote RPI without asking for a the user password you’re set up.

Next in the HA terminal window you need to edit the configuration.yaml file

nano /root/config/configuration.yaml

Add the following lines at the end of the file

shell_command:
  wake_samsung_tv: '/config/shell_cmds/wake_samsung_tv'

If your configuration.yaml file already contains a shell_command line you DO NOT add it again, you simply add the second line under the existing shell_command line, maintaining the same indentation as your other shell_command lines. To enable this you need to restart HA core, which you can do in the HA GUI from “Developer Tools → YAML → RESTART”

From HACS I utilize the “SamsungTV Smart” intergration and the TV Remote Card (with touchpad and haptic feedback) to control the Samsung TV. I add a manual card to the HA GUI with the following contents

type: custom:stack-in-card
title: TV Controls
mode: vertical
cards:
  - type: custom:tv-card
    entity: media_player.backbed_samsung
    tv: true
    enable_button_feedback: false
    media_control_row:
      - samsung_power
      - mythtv
      - music
      - select_app
      - tv
    channel_row:
      - return
      - info
    navigation_row: buttons
    volume_row: buttons
    custom_keys:
      samsung_power:
        icon: mdi:power
        service: shell_command.wake_samsung_tv
      tv:
        icon: mdi:television-classic
        service: media_player.play_media
        service_data:
          media_content_id: KEY_SOURCE+KEY_LEFT+KEY_LEFT+KEY_LEFT+KEY_ENTER
          media_content_type: send_key
          entity_id: media_player.backbed_samsung
      mythtv:
        icon: mdi:television-play
        service: media_player.play_media
        service_data:
          media_content_id: KEY_SOURCE+KEY_LEFT+KEY_LEFT+KEY_LEFT+KEY_RIGHT+KEY_ENTER
          media_content_type: send_key
          entity_id: media_player.backbed_samsung
      music:
        icon: mdi:music
        service: media_player.play_media
        service_data:
          media_content_id: KEY_SOURCE+KEY_LEFT+KEY_LEFT+KEY_LEFT+KEY_RIGHT+KEY_RIGHT+KEY_ENTER
          media_content_type: send_key
          entity_id: media_player.backbed_samsung
      select_app:
        icon: mdi:apps
        service: media_player.play_media
        service_data:
          media_content_id: KEY_HOME
          media_content_type: send_key
          entity_id: media_player.backbed_samsung

You can see under samsung_power it calls shell_command.wake_samsung_tv to send the remote control power key from the remote RPI. If you have a samsung tv you would update all of the references above to media_player.backbed_samsung to be your tv’s entity ID. What I like about the custom TV card is it works well with little to no configuration for a samsung tv. It also allows the function associated for any and all keys to be overridden, so each button can be used to send a remote control button for a different TV if you desire.

The interface provided as configured above looks like this:

Clicking on the power button should turn the Samsung TV on and off by causing the remote control power button scancode to be transmitted.

The above is fine if you only have to send one or two keys, but if you have to send a lot of keys you might want to try a different way to interface between HA and the remote RPI3. Above I showed a few keys that were reported associated with an AV surround sound receiver. On the RPI3 that sits by this receiver I use the following script named proj-amp-networkControl.py to control the receiver and a projector. The script transmits IR commands to control the AV receiver and RS323 commands to control the Epson projector. The proj-amp-networkControl.py contents are here:

#!/usr/bin/python3
# License: Public Domain
from __future__ import division
import time
import sys
import socket
import os

# Uncomment to enable debug output.
#import logging
#logging.basicConfig(level=logging.DEBUG)

state="OFF"

#def feed_drop_servo():

# -- Main ----
HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 33333              # Arbitrary non-privileged port
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen(1)

    try:
        while True:
            conn, addr = s.accept()
            with conn:
                print('Connected by', addr)
                while True:
                    data = conn.recv(1024).decode("ascii")
                    if not data: break
                    #print('Data: ',data)

                    if data == "POWER":
                        print("Command read is POWER")
                        # Turns piorneer off (cycle power)
                        os.system("sudo ir-ctl -S nec:0xa51c")
                        # Turn projector off
                        os.system("/home/brian/epson-proj-serial-ctl.py OFF")
                        state="OFF"
                    elif data == "AMPPOWER":
                        print("Command read is AMPPOWER")
                        # Cycle POWER on AMP and leave state alone
                        os.system("sudo ir-ctl -S nec:0xa51c")

                    elif data == "UP":
                        print("Command read is UP")
                        # Turn volume up
                        os.system("sudo ir-ctl -S nec:0xa50a")
                        time.sleep(.1)
                        os.system("sudo ir-ctl -S nec:0xa50a")

                    elif data == "DOWN":
                        print("Command read is DOWN")
                        # Turn volume down
                        os.system("sudo ir-ctl -S nec:0xa50b")
                        time.sleep(.1)
                        os.system("sudo ir-ctl -S nec:0xa50b")

                    elif data == "MUTE":
                        print("Command read is MUTE")
                        os.system("sudo ir-ctl -S nec:0xa512")

                    elif data == "FIRE":
                        print("Command read is FIRE")
                        # If state is off then turn on amp for audio
                        if state == "OFF":
                            os.system("sudo ir-ctl -S nec:0xa51c")

                        # Now turn projector on for HDMI1
                        os.system("/home/brian/epson-proj-serial-ctl.py ON HDMI1")
                        # set AMP to input connected to FIRESTICK (DVD)
                        os.system("sudo ir-ctl -S nec:0xa585")
                        # Mark state as watching Firestick
                        state="FIRE"

                    elif data == "MYTH":
                        print("Command read is MYTH")
                        # If state is off then turn on amp for audio
                        if state == "OFF":
                            os.system("sudo ir-ctl -S nec:0xa51c")

                        # turn projector on and set to PC input
                        os.system("/home/brian/epson-proj-serial-ctl.py ON PC")
                        # set pioneer to PC connected input so sound plays (DVR/BVR)
                        os.system("sudo ir-ctl -S nec:0xa589")
                        # Mark state as watching Mythtv
                        state="MYTH"


                    elif data == "LMS":
                        print("Command read is LMS")
                        # If state is off then turn on amp for audio
                        if state == "OFF":
                            os.system("sudo ir-ctl -S nec:0xa51c")
                            # since we did turn on projector will have to wait for audio to come up
                            time.sleep(20)

                        # turn off projector
                        os.system("/home/brian/epson-proj-serial-ctl.py OFF")
                        # Set piorneer to input for music (CD)
                        os.system("sudo ir-ctl -S nec:0xa54c")
                        # Mark state as listening to Logitect Media Server (squeezebox music)
                        state="LMS"

                conn.close()
                sys.stdout.flush()
    finally:
        print("All done")

While you probable will not be controlling a projector via RS232, I’m including the script epson-proj-serial-ctl.py because it’s referenced above in the previous script. The script epson-proj-serial-ctl.py contains the following:

#!/usr/bin/env python
import sys
import time
import serial

ser = serial.Serial(
    #port='/dev/ttyAMA0',
    #port='/dev/ttyS0',
    port='/dev/ttyUSB0',
    baudrate = 9600,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    write_timeout=1, 
    xonxoff=False,
    rtscts=False,
    dsrdtr=False,
    timeout=1
)

arg_cnt = len(sys.argv)

if arg_cnt < 2:
    print("Usage:",sys.argv[0],"[ON|OFF]","[PC|HDMI1|HDMI2|COMPONENT|SVIDEO|VIDEO]")
    exit(0)

cmd = sys.argv[1]
if ((cmd != "ON" and cmd != "OFF") or (cmd == "ON" and arg_cnt < 3)):
    print("Usage:",sys.argv[0],"[ON|OFF]","[PC|HDMI1|HDMI2|COMPONENT|SVIDEO|VIDEO]")
    exit(0)

#Clear anything that might be in input buffer
readlen=40
while readlen == 40:
    x=ser.read(40)
    readlen = len(x)

if cmd == "OFF":
    full_cmd = ("PWR OFF\r").encode("ascii") 
    ser.write(full_cmd)

else: # Turning on projector if need be and setting input

    full_cmd = ("PWR?\r").encode("ascii")
    ser.write(full_cmd)

    x=ser.read(10)
    if len(x)>1:
        resp=(((x.decode("ascii")).split('\r'))[0]).split('=')[1]
        print("Response: ", resp )
        # 00 means projector is off so turn it on
        if resp == "00":
            print("Turning projector on") 
            full_cmd = ("PWR ON\r").encode("ascii")
            ser.write(full_cmd)
            time.sleep(30)
            # Clear out read buffer again
            readlen=40
            while readlen == 40:
                x=ser.read(40)
                readlen = len(x)

            # Need to wait until projector reports power on when asked to set input
            full_cmd = ("PWR?\r").encode("ascii")
            done = False
            while not done:
                # Send command to check power
                ser.write(full_cmd)
                # read response
                x=ser.read(30)
                if len(x)>1:
                    try:
                        resp=(((x.decode("ascii")).split('\r'))[0]).split('=')[1]
                        print("Response: ", resp )
                        # 01 means projector is on
                        if resp == "02":
                            done = True
                    except Exception:
                        pass

    else:
        print("No response to PWR? command, will exit")
        exit(0)

    source = sys.argv[2]
    print("Need to set source to",source)

    if source == "PC": 
        full_cmd = ("SOURCE 21\r").encode("ascii")
    elif source == "HDMI1":
        full_cmd = ("SOURCE 30\r").encode("ascii")
    elif source == "HDMI2":
        full_cmd = ("SOURCE A0\r").encode("ascii")
    elif source == "COMPONENT":
        full_cmd = ("SOURCE 14\r").encode("ascii")
    elif source == "SVIDEO":
        full_cmd = ("SOURCE 42\r").encode("ascii")
    elif source == "VIDEO":
        full_cmd = ("SOURCE 41\r").encode("ascii")
    else:
        print("Source",source,"not known")
        exit(0)

    ser.write(full_cmd)

These two scripts provide a service that runs on port 33333 and is started by the following line in /etc/rc.local

su -c '/home/brian/proj-amp-networkControl.py 2>&1 > /dev/null &' brian

On the HA side there is a simple shell command pioneer_ctl that handles sending commands to the remote pi IR transmitter service

#!/bin/bash
echo -n $1 | nc -w0 192.168.0.115 33333

The IP address would be the IP of the RPI the service is running on. The pioneer_ctl shell comand is made avilable to HA in the configuration.yaml file with the following line under the “shell_command:” directive

  pioneer_ctl: '/config/shell_cmds/pioneer_ctl {{ cmd }}'

Here’s then part of a custom card in the HA GUI that results in the transmission of IR signals

type: vertical-stack
cards:
  - type: horizontal-stack
    cards:
      - show_name: true
        show_icon: true
        type: custom:button-card
        size: 25%
        tap_action:
          action: call-service
          service: shell_command.pioneer_ctl
          service_data:
            cmd: MYTH
        icon: mdi:television-play
        name: Mythtv on
      - show_name: true
        show_icon: true
        type: custom:button-card
        size: 25%
        tap_action:
          action: call-service
          service: shell_command.pioneer_ctl
          service_data:
            cmd: FIRE
        name: Firestick On
        icon: mdi:fire-circle
      - show_name: true
        show_icon: true
        type: custom:button-card
        size: 25%
        tap_action:
          action: call-service
          service: shell_command.pioneer_ctl
          service_data:
            cmd: LMS
        icon: mdi:music
        name: Music On
      - show_name: true
        show_icon: true
        type: custom:button-card
        entity: binary_sensor.basement_amp_status
        size: 25%
        state:
          - value: 'on'
            color: green
            name: AMP 2 Off
            icon: mdi:power-plug-off
          - value: 'off'
            color: default
            name: AMP 2 On
            icon: mdi:power-plug-outline
        tap_action:
          action: call-service
          service: shell_command.pioneer_ctl
          service_data:
            cmd: AMPPOWER
  - type: horizontal-stack
    cards:
      - show_name: true
        show_icon: true
        type: custom:button-card
        size: 25%
        tap_action:
          action: call-service
          service: shell_command.pioneer_ctl
          service_data:
            cmd: POWER
        icon: mdi:power-plug-off
        name: All Off
      - show_name: true
        show_icon: true
        type: custom:button-card
        size: 25%
        tap_action:
          action: call-service
          service: shell_command.pioneer_ctl
          service_data:
            cmd: MUTE
        icon: mdi:volume-mute
        name: Mute
      - show_name: true
        show_icon: true
        type: custom:button-card
        size: 25%
        tap_action:
          action: call-service
          service: shell_command.pioneer_ctl
          service_data:
            cmd: DOWN
        icon: mdi:volume-minus
        name: Volume Down
      - show_name: true
        name: Volume Up
        show_icon: true
        type: custom:button-card
        size: 25%
        tap_action:
          action: call-service
          service: shell_command.pioneer_ctl
          service_data:
            cmd: UP
        icon: mdi:volume-plus

In the above you see that shell_command.pioneer_ctl is called in various places with on parameter being provided to identify the scancode to be sent on the remote RPI3. The custom buttons created from the code above look like this

image

The Amp 2 on button is a pretty interesting button as it gets the status or the amp using the current draw reported by a TP-Link wall plug. More details on how that is set up are available over in this post.

In my implementation I also use universal media player entries to integrate the above controls with squeezelite media player controls that support my full house audio/video system. For completeness I’ve include them below. The lines below are included in my configuration.yaml file under the “media_player:” line.

media_player:

  - platform: universal
    name: basement_mp_cmb
    children:
      - media_player.basement_snap
      - binary_sensor.basement_amp_status
    commands:
      turn_on:
        service: shell_command.pioneer_ctl
        data:
          cmd: LMS
      turn_off:
        service: shell_command.pioneer_ctl
        data:
          cmd: AMPPOWER
      volume_up:
        service: shell_command.pioneer_ctl
        data:
          cmd: UP
      volume_down:
        service: shell_command.pioneer_ctl
        data:
          cmd: DOWN
      media_play_pause:
        service: media_player.media_play_pause
        target:
          entity_id: media_player.basement_snap

    attributes:
      state: binary_sensor.basement_amp_status
      media_content_id: media_player.basement_snap|media_content_id
      media_title: media_player.basement_snap|media_title
      entity_picture: media_player.basement_snap|entity_picture

  - platform: universal
    name: basement_mp_cmb
    children:
      - media_player.basement_snap
      - binary_sensor.basement_amp_status
    commands:
      turn_on:
        service: shell_command.pioneer_ctl
        data:
          cmd: LMS
      turn_off:
        service: shell_command.pioneer_ctl
        data:
          cmd: AMPPOWER
      volume_up:
        service: shell_command.pioneer_ctl
        data:
          cmd: UP
      volume_down:
        service: shell_command.pioneer_ctl
        data:
          cmd: DOWN
      media_play_pause:
        service: media_player.media_play_pause
        target:
          entity_id: media_player.basement_snap

    attributes:
      state: binary_sensor.basement_amp_status
      media_content_id: media_player.basement_snap|media_content_id
      media_title: media_player.basement_snap|media_title
      entity_picture: media_player.basement_snap|entity_picture

Update: I’ve switched to using these IR transmitters as they transmit a significantly better distance. They work especially well if you use them with this circuit shown in step two here .

Seems this post was premature, there was an error in my testing so I need to do more testing to validate these transmitters are working better.

After additional testing it appears I only get an extra foot of range with these alternative IR LEDs.

Here’s an updated service I put on a raspberry pi to receive network request and transmit IR signals. The following was stored in the ir_blaster.py file for the pi user.

#!/usr/bin/python3
# License: Public Domain
from __future__ import division
import time
import sys
import socket
import os

# Uncomment to enable debug output.
#import logging
#logging.basicConfig(level=logging.DEBUG)

state="OFF"
TRANS_DEV="/dev/lirc0"
FILES="/home/pi/old_samsung/"


# -- Main ----
HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 33333              # Arbitrary non-privileged port
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen(1)

    try:
        while True:
            conn, addr = s.accept()
            with conn:
                print('Connected by', addr)
                while True:
                    data = conn.recv(1024).decode("ascii")
                    if not data: break
                    print('Data: ',data)

                    cmd = "ir-ctl -d " + TRANS_DEV + " --send="+FILES+data
                    print(cmd)

                    os.system("ir-ctl -d " + TRANS_DEV + " --send="+FILES+data)

                    if data == "POWER":
                        print("Command read is POWER")
                        # Turns piorneer off (cycle power)
                        os.system("sudo ir-ctl -S nec:0xa51c")
                        state="OFF"

                    elif data == "UP":
                        print("Command read is UP")
                        # Turn volume up
                        os.system("sudo ir-ctl -S nec:0xa50a")

                    elif data == "DOWN":
                        print("Command read is DOWN")
                        # Turn volume down
                        os.system("sudo ir-ctl -S nec:0xa50b")

                    elif data == "MUTE":
                        print("Command read is MUTE")
                        os.system("sudo ir-ctl -S nec:0xa512")

                    elif data == "FIRE":
                        print("Command read is FIRE")
                        # If state is off then turn on amp for audio
                        if state == "OFF":
                            os.system("sudo ir-ctl -S nec:0xa51c")

                        # set AMP to input connected to FIRESTICK (DVD)
                        os.system("sudo ir-ctl -S nec:0xa585")
                        # Mark state as watching Firestick
                        state="FIRE"

                    elif data == "MYTH":
                        print("Command read is MYTH")
                        # If state is off then turn on amp for audio
                        if state == "OFF":
                            os.system("sudo ir-ctl -S nec:0xa51c")

                        # set pioneer to PC connected input so sound plays (DVR/BVR)
                        os.system("sudo ir-ctl -S nec:0xa589")
                        # Mark state as watching Mythtv
                        state="MYTH"


                    elif data == "LMS":
                        print("Command read is LMS")
                        # If state is off then turn on amp for audio
                        if state == "OFF":
                            os.system("sudo ir-ctl -S nec:0xa51c")
                            # since we did turn on projector will have to wait for audio to come up
                            time.sleep(20)

                        # Set piorneer to input for music (CD)
                        os.system("sudo ir-ctl -S nec:0xa54c")
                        # Mark state as listening to Logitect Media Server (squeezebox music)
                        state="LMS"

                conn.close()
                sys.stdout.flush()
    finally:
        print("All done")

I started this service by adding the following to rc.local:

runuser -l pi -c '/home/pi/ir_blaster.py  >/dev/null 2>&1 &'

The service above will listen for commands on port 33333. This line in the python code tells the service where to find capture IR signal files.

FILES="/home/pi/old_samsung/"

The IR signal was captured using commands like the following.

ir-ctl -d /dev/lirc0 --mode2 --receive=play.key

You run the above command to capture the signal for one button at a time. After you press the button on the remote you use c to quit the command. You then need to remove the timeout line at the end of the .key file that gets created.

I store the following in an HA shell command named send_control. It will send a command over to the IR transmit service.

#!/bin/bash
#echo Parms are $1 $2>> send_ctrl.out
if [ $1 == "master_tv" ]; then
	ip="192.168.10.16"
elif [ $1 == "back_bed_tv" ]; then
	ip="192.168.10.4"
else
        #echo device not known $1 >> send_ctrl.out
        exit
fi

echo -n $2 | nc -w0 $ip 33333

This shell command takes two parameters. The first parameter HA has to provide identifies the device, which is translated to the IP of the machine running the IR transmit service. You need to adjust this shell command to match your setup. The second parameter provides the name of the .key file that HA want’s the IR service to transmit.

In the configuration.yaml file you need to add this entry under your “shell_command:” section. If you don’t have a shell_command section you also then need to add the “shell_command:” line.

send_control: '/config/shell_cmds/send_control {{ device }} {{ cmd }}'

I currently use this with the custom:generic-remote-control-card that I obtained from the HACS frontend section. This is the configuration for that card that implements a samsung remote for an old non-web smart samsung tv

buttons:
  back:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: back.key
  bottom:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: down.key
  zero:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 0.key
  one:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 1.key
  two:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 2.key
  three:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 3.key
  four:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 4.key
  five:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 5.key
  six:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 6.key
  seven:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 7.key
  eight:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 8.key
  nine:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: 9.key
  exit:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: exit.key
  info:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: info.key
  left:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: left.key
  menu:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: menu.key
  mute:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: mute.key
  ok:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: enter.key
  power:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: power.key
  right:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: right.key
  source:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: source.key
  top:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: up.key
  volmin:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: voldown.key
  volplus:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: volup.key
  channelup:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: chanup.key
  channeldown:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: chandown.key
  guide:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: guide.key
  fastforward:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: fast_forward.key
  rewind:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: fast_back.key
  play:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: play.key
  pause:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: pause.key
  lastch:
    call: shell_command.send_control
    data:
      device: master_tv
      cmd: prev_chan.key
name: Samsung
remote_template: samsungtv
style: |
  ha-card {
    font-size: 10px;
  }
  h1{
    text-align:left;
    margin-left: 30px;
  }
type: custom:generic-remote-control-card

This interface then looks like this on the HA GUI.