In the post above the tv is an recent LG tv with the webOS operating system. There is a custom remote card that works great with the LG tv. All other devices connected to this tv, other than mythfrontend, can be controlled via HDMI CEC. As such there was no need for a number pad tided to the mythfrontend remote interface. In the basement we have a amplifier and Epson projector for watching movies. It has a firestick for accessing apps and a mythfrontend connection. So to watch live TV from cable you need a number pad to change channels. The amplifier and projector are old enough so they don’t have network based controls. Setting this area up took a little more effort. I already had LMS setup, with squeezelite running on a raspberry pi 3 to play music on the amplifier. Setting up LMS is documented here. This is the interface I now have in place to control media in my rec room.
I switch TV cards to this one as it works with this samsung integration that I have in my system. Both are available via HACS.
The following provides the full mythfrontend card with number pad, while also providing controls for the projector and amplifier. This is just the left hand side of the page above.
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
size: 25%
tap_action:
action: call-service
service: shell_command.pioneer_ctl
service_data:
cmd: AMPPOWER
icon: mdi:power-cycle
name: Cycle AMP
- 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
- type: custom:tv-card
entity: sun.sun
title: Basement Mythtv
tv: true
enable_button_feedback: false
channel_row:
- return
- menu
- info
- home
navigation_row: buttons
media_control_row:
- rewind
- play
- fast_forward
custom_keys:
return:
icon: mdi:arrow-left
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-escape
menu:
icon: mdi:menu
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-D
info:
icon: mdi:information-variant
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-I
home:
icon: mdi:home
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: JUMP-mainmenu
enter:
icon: mdi:checkbox-blank-circle
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-return
left:
icon: mdi:chevron-left
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-left
right:
icon: mdi:chevron-right
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-right
up:
icon: mdi:chevron-up
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-up
down:
icon: mdi:chevron-down
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-down
volume_up:
icon: mdi:volume-plus
service: shell_command.pioneer_ctl
service_data:
cmd: UP
play:
icon: mdi:play-pause
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: PS-P
rewind:
icon: mdi:rewind
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: PS-R
fast_forward:
icon: mdi:fast-forward
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: PS-F
- type: vertical-stack
cards:
- type: grid
cards:
- type: custom:button-card
size: 30%
show_name: true
show_icon: true
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-1
icon: mdi:numeric-1
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-2
icon: mdi:numeric-2
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-3
icon: mdi:numeric-3
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-4
icon: mdi:numeric-4
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-5
icon: mdi:numeric-5
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-6
icon: mdi:numeric-6
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-7
icon: mdi:numeric-7
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-8
icon: mdi:numeric-8
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-9
icon: mdi:numeric-9
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-escape
name: Cancel
show_name: false
icon: mdi:close
- type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: KEY-0
icon: mdi:numeric-0
- show_name: false
show_icon: true
type: custom:button-card
size: 30%
tap_action:
action: call-service
service: shell_command.send_myth_ctl
service_data:
device: basement_myth
cmd: backspace
name: Backspace
icon: mdi:backspace-outline
square: false
The modified shell_command.send_myth_ctl script accessed in the HA card above is here:
#!/bin/bash
echo Parms are $1 $2>> myth.out
if [ $1 == "family_room_myth" ]; then
ip="192.168.0.8"
port="6546"
elif [ $1 == "back_bed_myth" ]; then
ip="192.168.0.4"
port="6546"
elif [ $1 == "basement_myth" ]; then
ip="192.168.0.8"
port="6550"
else
echo Myth device not known $1 >> myth.out
exit
fi
# Make sure shared memory variable exist for tracking playback speed
if [ ! -f "/dev/shm/$1" ]; then
echo 1 > /dev/shm/$1
fi
if [ ! -z "$2" ]; then
#echo run stop_client on $ip
IFS='-'
#Read the split words into an array based on dash delimiter
read -a fields <<< "$2"
if [[ "${fields[0]}" = PS ]]; then
curspeed=`cat /dev/shm/$1`
# echo Current speed is $curspeed >> myth.out
if [[ "${fields[1]}" = P ]]; then
if [[ $curspeed = 1 ]]; then
curspeed=0
else
curspeed=1
fi
elif [[ "${fields[1]}" = F ]]; then
if [[ $curspeed < 1 ]]; then
curspeed=2
else
((curspeed++1))
if [[ $curspeed > 5 ]]; then
curspeed=5
fi
fi
elif [[ "${fields[1]}" = R ]]; then
curspeed="1/2"
fi
echo "echo play speed "$curspeed"x| nc $ip $port -w 0" >> myth.out
echo "play speed "$curspeed"x"| nc $ip $port -w 0
echo $curspeed > /dev/shm/$1
elif [[ "${fields[0]}" = KEY ]]; then
echo key ${fields[1]} | nc $ip $port -w 0
elif [[ "${fields[0]}" = CTL ]]; then
if [[ "${fields[1]}" = SHUTDOWN ]]; then
ssh -i /config/.ssh/id_rsa -o StrictHostKeyChecking=no brian@$ip sudo shutdown -h now
elif [[ "${fields[1]}" = START_MYTH ]]; then
ssh -i /config/.ssh/id_rsa -o StrictHostKeyChecking=no brian@$ip ./run_mythfrontend
fi
else
#echo "echo key ${fields[1]} | nc $ip $port -w 0" >> myth.out
echo jump ${fields[1]} | nc $ip $port -w 0
fi
fi
On the RPI 3 running squeezelite I enabled infrared transmission in the /boot/config.txt file:
dtoverlay=gpio-ir-tx,gpio_pin=18
Then I connected an IR LED to pin 18 and a ground GPIO pin. This was done to control the pioneer amplifier via IR. This method of connecting the IR LED directly to the gpio pin doesn’t transmit very far,so I put the IR LED right in front of the amplifier. If I had a better IR transmitter I would have used it to control the Epson projector also. The Epson projector supports control communications over RS232. I attempted to use the RPI 3 com port connect to the projector’s RS232 interface but this didn’t work. I think the issue is that the RPI com port is at 3v and RS232 spec calls for 5-15v. I had a usb RS232 device sitting around so I plugged it into the squeezelite RPI and it works great talking and controlling the Epson projector. On HA I have this simple shell command “pioneer_ctl” to send commands over to the RPI.
#!/bin/bash
echo -n $1 | nc -w0 192.168.0.25 33333
If you look at the HA interface card above you’ll see this command being called. On the RPI I have a service named proj-amp-networkControl.py which handles these commands. It’s 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"
# -- 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/pi/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/pi/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/pi/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/pi/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")
I start this script by adding the following line into /etc/rc.local
su -c '/home/pi/proj-amp-networkControl.py 2>&1 > /dev/null &' pi
The user pi has to be in the dialout group to access the RS232 interface. The above script calls the
epson-proj-serial-ctl.py to implement the projector controls over RS232:
#!/usr/bin/env python
import sys
import time
import serial
ser = serial.Serial(
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)
print("Good command", cmd)
#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)
With this I now have the ability to control the mythfrontend and control the amplifier and projector that provide the audio/visual.