sonytv/sonytv.sh

496 lines
12 KiB
Bash
Raw Normal View History

2021-09-16 11:24:09 +10:00
#!/bin/bash
BOLD="\033[1m"
ITALIC="\033[3m"
STRIKE="\033[9m"
PLAIN="\033[0m"
UNDERLINE="\033[4m"
RED="\033[31m"
MAGENTA="\033[35m"
GREEN="\033[32m"
YELLOW="\033[33m"
BLUE="\033[34m"
CYAN="\033[36m"
GREY="\033[2;37m"
LINK="$BLUE$UNDERLINE"
IRCC_OFF="AAAAAQAAAAEAAAAvAw=="
IRCC_VOLUP="AAAAAQAAAAEAAAASAw=="
errorstr=""
timeout="2"
timeout="-m $timeout"
CONFIGFILE="${HOME}/.sonytvrc"
pskfile="$HOME/.sonytvpsk"
[[ -e $pskfile ]] && psk=$(cat $pskfile) || psk="1111"
wolok=0
verbose=0
VALID_COMMANDS=" on off methods ircc raw ver status setup channel slowoff "
function real_docurl() {
local method httpmethod fullurl data rv arg reqtype
reqtype="$1"
httpmethod="$2"
method="$3"
arg="$4"
if [[ $reqtype == "method" ]]; then
fullurl="$URL/sony/system"
data="{\"id\": 20, \"method\": \"${method}\", \"version\": \"1.0\", \"params\": [\"${arg}\"]}"
if [[ $verbose -eq 1 ]]; then
echo RUNNING curl -u "":${psk} $curlopts -X $httpmethod -H 'Content-Type: application/json' -H "X-Auth-PSK: ${psk}" "$fullurl" -d "$data" >/dev/stderr
fi
curl -u "":${psk} $timeout -s -X $httpmethod -H 'Content-Type: application/json' -H "X-Auth-PSK: ${psk}" -d "$data" $fullurl
rv=$?
else
fullurl="$URL/sony/IRCC"
curl -u "":${psk} $timeout -s -X $httpmethod -H 'Content-Type: application/json' -H "X-Auth-PSK: ${psk}" $fullurl -d @- <<EOF
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:X_SendIRCC xmlns:u="urn:schemas-sony-com:service:IRCC:1">
<IRCCCode>$arg</IRCCCode>
</u:X_SendIRCC>
</s:Body>
</s:Envelope>
EOF
rv=$?
fi
return $rv
}
function runcurl() {
local method rv httpmethod arg reqtype
reqtype=$1
httpmethod=$2
method=$3
arg=$4
errorstr=""
resultstr=$(real_docurl $reqtype $httpmethod "$method" "$arg")
rv=$?
[[ $verbose -eq 1 ]] && echo "Curl retval: $rv"
[[ $verbose -eq 1 ]] && echo "Result: $resultstr"
[[ $verbose -eq 1 ]] && echo "Error: $errorstr"
if [[ $resultstr == *rror* ]]; then
errorstr="TV returned '$(echo "$resultstr" | grep errorDescription | sed -e 's/<errorDescription>//;s,</error.*,,;s/^\s*//' )'"
rv=1
elif [[ $rv -ne 0 ]]; then
errorstr="curl failed (timeout?) $resultstr"
else
errorstr=""
fi
return $rv
}
function usage() {
local x this
echo "usage: $0 [OPTIONS] command [count]"
echo
echo " -w secs Wait x seconds between ircc sends."
echo
echo " Valid commands are:"
echo " $VALID_COMMANDS"
echo
echo " Valid shortcuts are:"
echo
format=" %-12s -> %s"
for x in ${!alts[@]}; do
printf "$format" "${alts[$x]}" "${altd[$x]}"
if [[ ! -z ${altarg[$x]} ]]; then
for this in ${altarg[$x]}; do
echo -n " ${this}"
done
fi
echo
done
}
function action() {
echo -e "$BOLD$GREEN* $PLAIN$GREEN$*$PLAIN"
}
function error() {
echo -e "$BOLD${RED}ERROR: $PLAIN$RED$*$PLAIN" >/dev/stderr
}
function info() {
echo -e "$BOLD${CYAN}>> $PLAIN$CYAN$*$PLAIN"
}
function register_device() {
local me myid initpsk opts
me=$(uname -n)
myid="12345"
initpsk=$1
if [[ -z $initpsk ]]; then
action "Requesting registration from TV..."
curl -s "$URL/sony/accessControl" -o /dev/null -d @- <<EOF
{"id":13,"method":"actRegister","version":"1.0","params":[{"clientid":"$myid","nickname":"$me"},[{"clientid":"$myid","value":"yes","nickname":"$me","function":"WOL"}]]}
EOF
else
#-u "":${initpsk}
#-H "X-Auth-PSK: ${initpsk}"
action "Registering with TV..."
res=$(curl -s -u "":${initpsk} "$URL/sony/accessControl" -d @- <<EOF
{"id":13,"method":"actRegister","version":"1.0","params":[{"clientid":"$myid","nickname":"$me"},[{"clientid":"$myid","value":"yes","nickname":"$me","function":"WOL"}]]}
EOF
)
if [[ "$res" == *rror* ]]; then
error "registration failed"
else
info "Registration successful! PSK set to ${BOLD}$psk"
echo "$psk" >"$pskfile"
info "PSK written to $pskfile"
fi
fi
}
function addalt() {
alts[$nalts]=$1
altd[$nalts]=$2
shift 2
altarg[$nalts]="$*"
nalts=$((nalts + 1))
}
function numtoircc() {
case $1 in
1) echo "AAAAAQAAAAEAAAAAAw==";;
2) echo "AAAAAQAAAAEAAAABAw==";;
3) echo "AAAAAQAAAAEAAAACAw==";;
4) echo "AAAAAQAAAAEAAAADAw==";;
5) echo "AAAAAQAAAAEAAAAEAw==";;
6) echo "AAAAAQAAAAEAAAAFAw==";;
7) echo "AAAAAQAAAAEAAAAGAw==";;
8) echo "AAAAAQAAAAEAAAAHAw==";;
9) echo "AAAAAQAAAAEAAAAIAw==";;
0) echo "AAAAAQAAAAEAAAAJAw==";;
11) echo "AAAAAQAAAAEAAAAKAw==";;
12) echo "AAAAAQAAAAEAAAALAw==";;
*) ;;
esac
}
function channametonum() {
case $1 in
"abc") echo "22";;
"jazz") echo "201";;
"win") echo "80";;
*) echo "$1";;
esac
}
IRCC_DELAY=0.4
addalt "volup" "ircc" "VolumeUp"
addalt "vu" "ircc" "VolumeUp"
addalt "voldown" "ircc" "VolumeDown"
addalt "vu" "ircc" "VolumeUp"
addalt "vd" "ircc" "VolumeDown"
addalt "ir" "ircc"
addalt "chan" "channel"
addalt "c" "channel"
addalt "soff" "slowoff"
ARGS="hp:vw:"
while getopts "$ARGS" i; do
case "$i" in
h)
usage;
exit 1;
;;
p)
psk="$OPTARG";
;;
v)
verbose=1
;;
w)
IRCC_DELAY="$OPTARG";
;;
*)
error "invalid argument: $i";
usage;
;;
esac
done
shift $((OPTIND - 1))
if [[ $# -lt 1 ]]; then
usage
exit 1
fi
cmd=$1
shift
nargs=0
# resolve shortcuts
for x in ${!alts[@]}; do
if [[ ${alts[$x]} == $cmd ]]; then
cmd=${altd[$x]}
if [[ ! -z ${altarg[$x]} ]]; then
nargs=0
for this in ${altarg[$x]}; do
arg[$nargs]=${this}
nargs=$((nargs + 1))
done
fi
fi
done
while [[ $# -ge 1 ]]; do
arg[$nargs]="$1"
nargs=$((nargs + 1))
shift
done
if [[ $VALID_COMMANDS != *\ $cmd\ * ]]; then
error "'$cmd' is not a valid command."
exit 1
fi
# read config file
if [[ $cmd != "setup" ]]; then
if [[ -e $CONFIGFILE ]]; then
source $CONFIGFILE
else
error "Can't find config file ($CONFIGFILE) - use '$0 setup' to create it."
exit 1
fi
fi
if which -s wakeonlan; then
if [[ ! -z $MAC ]]; then
wolok=1
fi
fi
#for x in ${!arg[@]}; do
# echo "arg $x = '${arg[$x]}'"
#done
#exit
sendcount=1
if [[ $cmd == "upnp" ]]; then
info "UPnP info from TV:"
fullurl="$URL:52323/dmr.xml"
curlarg=""
res=$(curl $timeout -s "$fullurl")
rv=$?
if [[ $rv -eq 0 ]]; then
if which -s xq; then
res=$(echo "$res" | xq '.root.device."av:X_IRCCCodeList"')
fi
echo "$res"
else
error "curl to $fullurl failed"
fi
exit $rv
elif [[ $wolok -eq 1 && $cmd == "on" ]]; then
if [[ $wolok -eq 1 ]]; then
n=0
while [[ $n -lt 3 ]]; do
wakeonlan ${MAC} >/dev/null
n=$((n + 1))
done
else
error "'wakeonlan' binary is not available - please install it"
exit 1
fi
elif [[ $cmd == "channel" ]]; then
if [[ -z ${arg[0]} ]]; then
error "'channel' command requires an argument"
exit 1
fi
cname=${arg[0]}
cnum=$(channametonum $cname)
if [[ $cname == $cnum ]]; then
action "Changing channel to $BOLD$cname"
else
action "Changing channel to $BOLD$cname$PLAIN$GREEN ($cnum)"
fi
for (( i=0; i<${#cnum}; i++ )); do
num=${cnum:$i:1}
code=$(numtoircc $num)
[[ ! -z $code ]] && runcurl ircc POST "" $code
sleep $IRCC_DELAY
done
info "Done"
elif [[ $cmd == "slowoff" ]]; then
if [[ -z ${arg[0]} ]]; then
2022-03-05 09:59:09 +11:00
waittime=2.0
2021-09-16 11:24:09 +10:00
else
waittime=${arg[0]}
fi
if [[ -z ${arg[1]} ]]; then
count=60
else
count=${arg[1]}
fi
# slowly drop volume
code="AAAAAQAAAAEAAAATAw=="
action "Slowly dropping volume on $HOST..."
n=0
while [[ $n -lt $count ]]; do
runcurl ircc POST "" $code
sleep $waittime
n=$((n + 1))
done
curlarg="$IRCC_OFF"
reqtype="ircc"
what="PowerOff"
httpmethod="POST"
jqs=""
action "Sending $what to $HOST${ntimes}..."
runcurl $reqtype $httpmethod "$method" $curlarg
info "Done"
else
reqtype="method"
jqs="try (.results[]) // try (.result[]) // ."
if [[ $cmd == "off" ]]; then
curlarg="$IRCC_OFF"
reqtype="ircc"
what="IRCC command PowerOff - $curlarg"
httpmethod="POST"
jqs=""
elif [[ $cmd == "ircc" ]]; then
if [[ -z ${arg[1]} ]]; then
sendcount=1
else
sendcount=${arg[1]}
fi
if [[ -z ${arg[0]} ]]; then
method="getRemoteControllerInfo"
what="List IRCC Commands"
httpmethod="POST"
jqs=".result[1][] | \"\(.name): \(.value)\""
elif [[ ${arg[0]} =~ AAA.* ]]; then
curlarg="${arg[0]}"
reqtype="ircc"
what="Raw IRCC command '${arg[0]}'"
httpmethod="POST"
jqs=""
else
runcurl "method" POST getRemoteControllerInfo
code=$(echo "$resultstr" | jq -r ".result[1][] | select(.name == \"${arg[0]}\") | .value")
if [[ $code =~ AAA.* ]]; then
curlarg="$code"
reqtype="ircc"
#what="IRCC command ${arg[0]} ($code)"
what="IRCC command ${arg[0]}"
httpmethod="POST"
jqs=""
else
error "No IRCC code found for '${arg[0]}'"
exit 1
fi
fi
elif [[ $cmd == "status" ]]; then
method="getPowerStatus"
what="Query Power"
httpmethod="POST"
elif [[ $cmd == "methods" ]]; then
method="getMethodTypes"
what="List Commands"
httpmethod="POST"
jqs=".results[] | .[0]"
elif [[ $cmd == "ver" ]]; then
method="getInterfaceInformation"
what="Query Version"
httpmethod="POST"
elif [[ $cmd == "setup" ]]; then
read -p "Enter IP/hostname of TV> " HOST
ping -t 1 -c 2 $HOST >/dev/null
sleep 1
arpres=$(arp -n ${HOST})
if [[ $? -ne 0 ]]; then
error "Can't determine MAC address for $HOST."
exit 1
fi
MAC=$(echo "$arpres" | awk '{ print $4 }')
URL="http://$HOST"
echo "TV hostname: $HOST"
echo "TV MAC address: $MAC"
echo "TV URL: $URL"
echo
read -p "Is the above correct (y/n)? " yn
if [[ $yn != "y" ]]; then
echo "Aborting."
exit 1
fi
cat > $CONFIGFILE <<EOF
HOST="$HOST"
MAC="$MAC"
URL="$URL"
EOF
info "Config file created in $CONFIGFILE"
register_device
info "Enter the PIN shown on your TV:"
read -p "> " psk
register_device $psk
exit 0
elif [[ $cmd == "raw" ]]; then
if [[ -z ${arg[0]} ]]; then
error "'raw' command requires an argument"
exit 1
fi
method="${arg[0]}"
what="${arg[0]}"
httpmethod="POST"
fi
n=0
if [[ $sendcount -gt 1 ]]; then
ntimes=" $sendcount times"
else
ntimes=""
fi
err=0
action "Sending $what to $HOST${ntimes}..."
allresults=""
while [[ $n -lt $sendcount ]]; do
runcurl $reqtype $httpmethod "$method" $curlarg
rv=$?
if [[ $rv -eq 0 ]]; then
if [[ $sendcount -gt 1 ]]; then
echo -en "${YELLOW}*${BOLD}$((n + 1))${PLAIN}${YELLOW}* ${PLAIN}"
else
info "Success"
fi
if [[ ! -z $resultstr ]]; then
if [[ ! -z $jqs ]]; then
allresults=""
append=$(echo "$resultstr" | jq -r "${jqs}" | sed 's/^/ /')
allresults=$(printf "%s\n%s\n" "$allresults" "$append")
fi
fi
else
error "$rv: $errorstr"
err=1
break
fi
if [[ $n -ge 1 ]]; then
sleep $IRCC_DELAY
fi
n=$((n + 1))
done
if [[ $sendcount -gt 1 ]]; then
echo
info "Success"
fi
if [[ ! -z $allresults ]]; then
echo "$allresults"
fi
fi
exit 0