2017-12-08 10:03:23 +01:00
#!/bin/sh
#
# Amazon Alexa Remote Control (PLAIN shell)
# alex(at)loetzimmer.de
#
2019-01-23 00:07:58 +01:00
# 2019-01-22: v0.11 (for updates see http://blog.loetzimmer.de/2017/10/amazon-alexa-hort-auf-die-shell-echo.html)
2017-12-08 10:03:23 +01:00
#
###
#
# (no BASHisms were used, should run with any shell)
# - requires cURL for web communication
2018-01-10 08:24:36 +01:00
# - (GNU) sed and awk for extraction
2017-12-08 10:03:23 +01:00
#
##########################################
2019-01-23 00:07:58 +01:00
SET_EMAIL = 'amazon_account@email.address'
SET_PASSWORD = 'Very_Secret_Amazon_Account_Password'
2017-12-08 10:03:23 +01:00
2019-01-23 00:07:58 +01:00
SET_LANGUAGE = "de-DE"
#SET_LANGUAGE="en-US"
2017-12-08 10:03:23 +01:00
2019-01-23 00:07:58 +01:00
SET_AMAZON = 'amazon.de'
#SET_AMAZON='amazon.com'
SET_ALEXA = 'alexa.amazon.de'
#SET_ALEXA='pitangui.amazon.com'
2017-12-08 10:03:23 +01:00
# cURL binary
2019-01-23 00:07:58 +01:00
SET_CURL = '/usr/bin/curl'
2017-12-08 10:03:23 +01:00
# cURL options
# -k : if your cURL cannot verify CA certificates, you'll have to trust any
# --compressed : if your cURL was compiled with libz you may use compression
2018-03-09 18:53:33 +01:00
# --http1.1 : cURL defaults to HTTP/2 on HTTPS connections if available
2019-01-23 00:07:58 +01:00
SET_OPTS = '--compressed --http1.1'
#SET_OPTS='-k --compressed --http1.1'
2017-12-08 10:03:23 +01:00
2018-01-28 18:33:16 +01:00
# browser identity
2019-01-23 00:07:58 +01:00
SET_BROWSER = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:1.0) bash-script/1.0'
#SET_BROWSER='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0'
# tmp path
SET_TMP = "/tmp"
2018-01-28 18:33:16 +01:00
2017-12-08 10:03:23 +01:00
###########################################
# nothing to configure below here
#
2019-01-23 00:07:58 +01:00
# retrieving environment variables if any are set
EMAIL = ${ EMAIL :- $SET_EMAIL }
PASSWORD = ${ PASSWORD :- $SET_PASSWORD }
AMAZON = ${ AMAZON :- $SET_AMAZON }
ALEXA = ${ ALEXA :- $SET_ALEXA }
LANGUAGE = ${ LANGUAGE :- $SET_LANGUAGE }
BROWSER = ${ BROWSER :- $SET_BROWSER }
CURL = ${ CURL :- $SET_CURL }
OPTS = ${ OPTS :- $SET_OPTS }
TMP = ${ TMP :- $SET_TMP }
2017-12-08 10:03:23 +01:00
COOKIE = " ${ TMP } /.alexa.cookie "
DEVLIST = " ${ TMP } /.alexa.devicelist.json "
DEVTXT = " ${ TMP } /.alexa.devicelist.txt "
GUIVERSION = 0
LIST = ""
LOGOFF = ""
COMMAND = ""
2018-06-18 17:56:30 +02:00
TTS = ""
SEQUENCECMD = ""
2017-12-08 10:03:23 +01:00
STATIONID = ""
QUEUE = ""
SONG = ""
2018-06-18 17:56:30 +02:00
ALBUM = ""
ARTIST = ""
2017-12-08 10:03:23 +01:00
ASIN = ""
SEEDID = ""
HIST = ""
LEMUR = ""
CHILD = ""
PLIST = ""
BLUETOOTH = ""
2018-03-01 22:35:22 +01:00
LASTALEXA = ""
2017-12-08 10:03:23 +01:00
usage( )
{
2019-01-23 00:07:58 +01:00
echo " $0 [-d <device>|ALL] -e <pause|play|next|prev|fwd|rwd|shuffle|repeat|vol:<0-100>> | "
2018-06-18 17:56:30 +02:00
echo " -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -r <\"station name\"|stationid> |"
echo " -s <trackID|'Artist' 'Album'> | -t <ASIN> | -u <seedID> | -v <queueID> | -w <playlistId> |"
echo " -a | -m <multiroom_device> [device_1 .. device_X] | -lastalexa | -l | -h"
echo
echo " -e : run command, additional SEQUENCECMDs:"
echo " weather,traffic,flashbriefing,goodmorning,singasong,tellstory,speak:'<text>'"
2017-12-08 10:03:23 +01:00
echo " -q : query queue"
2018-01-10 08:24:36 +01:00
echo " -b : connect/disconnect/list bluetooth device"
2017-12-08 10:03:23 +01:00
echo " -r : play tunein radio"
2018-06-18 17:56:30 +02:00
echo " -s : play library track/library album"
2017-12-08 10:03:23 +01:00
echo " -t : play Prime playlist"
echo " -u : play Prime station"
echo " -v : play Prime historical queue"
echo " -w : play library playlist"
echo " -a : list available devices"
echo " -m : delete multiroom and/or create new multiroom containing devices"
2018-03-01 22:35:22 +01:00
echo " -lastalexa : print serial number that received the last voice command"
2017-12-08 10:03:23 +01:00
echo " -l : logoff"
echo " -h : help"
}
while [ " $# " -gt 0 ] ; do
case " $1 " in
-d)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
DEVICE = $2
shift
; ;
-e)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
COMMAND = $2
shift
; ;
-b)
if [ " ${ 2 #- } " = " ${ 2 } " -a -n " $2 " ] ; then
BLUETOOTH = $2
shift
else
BLUETOOTH = "null"
fi
; ;
-m)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
LEMUR = $2
shift
while [ " ${ 2 #- } " = " ${ 2 } " -a -n " $2 " ] ; do
CHILD = " ${ CHILD } ${ 2 } "
shift
done
; ;
-r)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
STATIONID = $2
shift
; ;
-s)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
SONG = $2
shift
2018-06-18 17:56:30 +02:00
if [ " ${ 2 #- } " = " ${ 2 } " -a -n " $2 " ] ; then
ALBUM = $2
ARTIST = $SONG
shift
fi
2017-12-08 10:03:23 +01:00
; ;
-t)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
ASIN = $2
shift
; ;
-u)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
SEEDID = $2
shift
; ;
-v)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
HIST = $2
shift
; ;
-w)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
PLIST = $2
shift
; ;
-d)
if [ " ${ 2 #- } " != " ${ 2 } " -o -z " $2 " ] ; then
echo " ERROR: missing argument for ${ 1 } "
usage
exit 1
fi
DEVICE = $2
shift
; ;
-l)
LOGOFF = "true"
; ;
-a)
LIST = "true"
; ;
-q)
QUEUE = "true"
; ;
2018-03-01 22:35:22 +01:00
-lastalexa)
LASTALEXA = "true"
; ;
2017-12-08 10:03:23 +01:00
-h| -\? | --help)
usage
exit 0
; ;
*)
echo " ERROR: unknown option ${ 1 } "
usage
exit 1
; ;
esac
shift
done
case " $COMMAND " in
pause)
COMMAND = '{"type":"PauseCommand"}'
; ;
play)
COMMAND = '{"type":"PlayCommand"}'
; ;
next)
COMMAND = '{"type":"NextCommand"}'
; ;
prev)
COMMAND = '{"type":"PreviousCommand"}'
; ;
fwd)
COMMAND = '{"type":"ForwardCommand"}'
; ;
rwd)
COMMAND = '{"type":"RewindCommand"}'
; ;
shuffle)
COMMAND = '{"type":"ShuffleCommand","shuffle":"true"}'
; ;
2019-01-23 00:07:58 +01:00
repeat)
COMMAND = '{"type":"RepeatCommand","repeat":true}'
; ;
2017-12-08 10:03:23 +01:00
vol:*)
VOL = ${ COMMAND ##* : }
# volume as integer!
if [ $VOL -le 100 -a $VOL -ge 0 ] ; then
COMMAND = '{"type":"VolumeLevelCommand","volumeLevel":' ${ VOL } '}'
else
echo "ERROR: volume should be an integer between 0 and 100"
usage
exit 1
fi
; ;
2018-06-18 17:56:30 +02:00
speak:*)
SEQUENCECMD = 'Alexa.Speak'
2018-06-18 20:27:31 +02:00
TTS = $( echo ${ COMMAND ##* : } | sed -r 's/[^-a-zA-Z0-9_,?! ]//g' | sed 's/ /_/g' )
2018-06-18 17:56:30 +02:00
TTS = " ,\\\"textToSpeak\\\":\\\" ${ TTS } \\\" "
; ;
weather)
SEQUENCECMD = 'Alexa.Weather.Play'
; ;
traffic)
SEQUENCECMD = 'Alexa.Traffic.Play'
; ;
flashbriefing)
SEQUENCECMD = 'Alexa.FlashBriefing.Play'
; ;
goodmorning)
SEQUENCECMD = 'Alexa.GoodMorning.Play'
; ;
singasong)
SEQUENCECMD = 'Alexa.SingASong.Play'
; ;
tellstory)
SEQUENCECMD = 'Alexa.TellStory.Play'
; ;
2017-12-08 10:03:23 +01:00
"" )
; ;
*)
echo " ERROR: unknown command \" ${ COMMAND } \"! "
usage
exit 1
; ;
esac
#
# Amazon Login
#
log_in( )
{
################################################################
#
# following headers are required:
# Accept-Language (possibly for determining login region)
# User-Agent (CURL wouldn't store cookies without)
#
################################################################
rm -f ${ DEVLIST }
rm -f ${ DEVTXT }
rm -f ${ COOKIE }
#
# get first cookie and write redirection target into referer
#
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -D " ${ TMP } /.alexa.header " -c ${ COOKIE } -b ${ COOKIE } -A " ${ BROWSER } " -H " Accept-Language: ${ LANGUAGE } " -H "DNT: 1" -H "Connection: keep-alive" -H "Upgrade-Insecure-Requests: 1" -L\
2017-12-08 10:03:23 +01:00
https://alexa.${ AMAZON } | grep "hidden" | sed 's/hidden/\n/g' | grep "value=\"" | sed -r 's/^.*name="([^"]+)".*value="([^"]+)".*/\1=\2\&/g' > " ${ TMP } /.alexa.postdata "
#
2018-03-01 22:35:22 +01:00
# login empty to generate session
2017-12-08 10:03:23 +01:00
#
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -c ${ COOKIE } -b ${ COOKIE } -A " ${ BROWSER } " -H " Accept-Language: ${ LANGUAGE } " -H "DNT: 1" -H "Connection: keep-alive" -H "Upgrade-Insecure-Requests: 1" -L\
2017-12-08 10:03:23 +01:00
-H " $( grep 'Location: ' ${ TMP } /.alexa.header | sed 's/Location: /Referer: /' ) " -d " @ ${ TMP } /.alexa.postdata " https://www.${ AMAZON } /ap/signin | grep "hidden" | sed 's/hidden/\n/g' | grep "value=\"" | sed -r 's/^.*name="([^"]+)".*value="([^"]+)".*/\1=\2\&/g' > " ${ TMP } /.alexa.postdata2 "
#
# login with filled out form
# !!! referer now contains session in URL
#
2018-03-01 22:35:22 +01:00
${ CURL } ${ OPTS } -s -D " ${ TMP } /.alexa.header2 " -c ${ COOKIE } -b ${ COOKIE } -A " ${ BROWSER } " -H " Accept-Language: ${ LANGUAGE } " -H "DNT: 1" -H "Connection: keep-alive" -H "Upgrade-Insecure-Requests: 1" -L\
-H " Referer: https://www. ${ AMAZON } /ap/signin/ $( awk " \$0 ~/. ${ AMAZON } .*session-id[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " --data-urlencode " email= ${ EMAIL } " --data-urlencode " password= ${ PASSWORD } " -d " @ ${ TMP } /.alexa.postdata2 " https://www.${ AMAZON } /ap/signin > " ${ TMP } /.alexa.login "
2017-12-08 10:03:23 +01:00
2018-03-01 22:35:22 +01:00
# check whether the login has been successful or exit otherwise
if [ -z " $( grep 'Location: https://alexa.*html' ${ TMP } /.alexa.header2) " ] ; then
echo "ERROR: Amazon Login was unsuccessful. Possibly you get a captcha login screen."
echo " Try logging in to https://alexa. ${ AMAZON } with your browser. In your browser "
echo " make sure to have all Amazon related cookies deleted and Javascript disabled!"
echo
echo " (For more information have a look at ${ TMP } /.alexa.login) "
rm -f ${ COOKIE }
rm -f " ${ TMP } /.alexa.header "
rm -f " ${ TMP } /.alexa.header2 "
rm -f " ${ TMP } /.alexa.postdata "
rm -f " ${ TMP } /.alexa.postdata2 "
exit 1
fi
2017-12-08 10:03:23 +01:00
#
# get CSRF
#
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -c ${ COOKIE } -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
https://${ ALEXA } /api/language > /dev/null
2018-03-01 22:35:22 +01:00
rm -f " ${ TMP } /.alexa.login "
2017-12-08 10:03:23 +01:00
rm -f " ${ TMP } /.alexa.header "
2018-03-01 22:35:22 +01:00
rm -f " ${ TMP } /.alexa.header2 "
2017-12-08 10:03:23 +01:00
rm -f " ${ TMP } /.alexa.postdata "
rm -f " ${ TMP } /.alexa.postdata2 "
}
#
# get JSON device list
#
get_devlist( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/devices-v2/device?cached=false " > ${ DEVLIST }
if [ ! -f ${ DEVTXT } ] ; then
cat ${ DEVLIST } | sed 's/\\\\\//\//g' | sed 's/[{}]//g' | awk -v k = "text" '{n=split($0,a,","); for (i=1; i<=n; i++) print a[i]}' | sed 's/\"\:\"/\|/g' | sed 's/[\,]/ /g' | sed 's/\"//g' > ${ DEVTXT }
fi
}
check_status( )
{
#
# bootstrap with GUI-Version writes GUI version to cookie
# returns among other the current authentication state
#
2019-01-23 01:00:37 +01:00
AUTHSTATUS = $( ${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L https://${ ALEXA } /api/bootstrap?version= ${ GUIVERSION } )
MEDIAOWNERCUSTOMERID = $( echo $AUTHSTATUS | sed -r 's/^.*"customerId":"([^,]+)",.*$/\1/g' )
AUTHSTATUS = $( echo $AUTHSTATUS | sed -r 's/^.*"authenticated":([^,]+),.*$/\1/g' )
2017-12-08 10:03:23 +01:00
if [ " $AUTHSTATUS " = "true" ] ; then
return 1
fi
return 0
}
#
# set device specific variables from JSON device list
#
set_var( )
{
ATTR = "accountName"
NAME = ` grep ${ ATTR } \| ${ DEVTXT } | sed " s/^.* ${ ATTR } |// " | sed 's/ /_/g' `
ATTR = "deviceType"
TYPE = ` grep ${ ATTR } \| ${ DEVTXT } | sed " s/^.* ${ ATTR } |// " | sed 's/ /_/g' `
ATTR = "serialNumber"
SERIAL = ` grep ${ ATTR } \| ${ DEVTXT } | sed " s/^.* ${ ATTR } |// " | sed 's/ /_/g' `
2019-01-23 01:00:37 +01:00
# ATTR="deviceOwnerCustomerId"
# MEDIAID=`grep ${ATTR}\| ${DEVTXT} | sed "s/^.*${ATTR}|//" | sed 's/ /_/g'`
2017-12-08 10:03:23 +01:00
ATTR = "deviceFamily"
FAMILY = ` grep ${ ATTR } \| ${ DEVTXT } | sed " s/^.* ${ ATTR } |// " | sed 's/ /_/g' `
if [ -z " ${ DEVICE } " ] ; then
# if no device was supplied, use the first Echo(dot) in device list
IDX = 0
for I in $FAMILY ; do
2018-01-25 10:38:42 +01:00
if [ " $I " = "ECHO" -o " $I " = "KNIGHT" -o " $I " = "ROOK" ] ; then
2017-12-08 10:03:23 +01:00
break;
fi
IDX = $(( IDX+1))
done
C = 0
for I in $NAME ; do
if [ $C -eq $IDX ] ; then
DEVICE = $I
break
fi
C = $(( C+1))
done
echo "setting default device to:"
echo ${ DEVICE }
else
DEVICE = ` echo $DEVICE | sed 's/ /_/g' `
IDX = 0
for I in $NAME ; do
if [ " $I " = " $DEVICE " ] ; then
break;
fi
IDX = $(( IDX+1))
done
fi
2019-01-23 01:00:37 +01:00
# customerId is now retrieved from the logged in user
# the customerId in the device list is always from the user registering the device initially
# C=0
# for I in $MEDIAID ; do
# if [ $C -eq $IDX ] ; then
# MEDIAOWNERCUSTOMERID=$I
# break
# fi
# C=$((C+1))
# done
2017-12-08 10:03:23 +01:00
C = 0
for I in $TYPE ; do
if [ $C -eq $IDX ] ; then
DEVICETYPE = $I
break
fi
C = $(( C+1))
done
C = 0
for I in $SERIAL ; do
if [ $C -eq $IDX ] ; then
DEVICESERIALNUMBER = $I
break
fi
C = $(( C+1))
done
if [ -z " ${ DEVICESERIALNUMBER } " ] ; then
echo " ERROR: unkown device dev: ${ DEVICE } "
exit 1
fi
}
#
# execute command
2018-06-18 17:56:30 +02:00
# (SequenceCommands by Michael Geramb and Ralf Otto)
2017-12-08 10:03:23 +01:00
#
run_cmd( )
{
2018-06-18 17:56:30 +02:00
if [ -n " ${ SEQUENCECMD } " ]
then
echo " Sequence command: ${ SEQUENCECMD } "
COMMAND = " {\"behaviorId\":\"PREVIEW\",\"sequenceJson\":\"{\\\"@type\\\":\\\"com.amazon.alexa.behaviors.model.Sequence\\\",\\\"startNode\\\":{\\\"@type\\\":\\\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\\\",\\\"type\\\":\\\" ${ SEQUENCECMD } \\\",\\\"operationPayload\\\":{\\\"deviceType\\\":\\\" ${ DEVICETYPE } \\\",\\\"deviceSerialNumber\\\":\\\" ${ DEVICESERIALNUMBER } \\\",\\\"locale\\\":\\\" ${ LANGUAGE } \\\",\\\"customerId\\\":\\\" ${ MEDIAOWNERCUSTOMERID } \\\" ${ TTS } }}}\",\"status\":\"ENABLED\"} "
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d ${ COMMAND } \
" https:// ${ ALEXA } /api/behaviors/preview "
else
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d ${ COMMAND } \
" https:// ${ ALEXA } /api/np/command?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } "
fi
2017-12-08 10:03:23 +01:00
}
#
# play TuneIn radio station
#
play_radio( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST\
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/tunein/queue-and-play?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } &guideId= ${ STATIONID } &contentType=station&callSign=&mediaOwnerCustomerId= ${ MEDIAOWNERCUSTOMERID } "
}
#
# play library track
#
play_song( )
{
2018-06-18 17:56:30 +02:00
if [ -z " ${ ALBUM } " ] ; then
JSON = " {\"trackId\":\" ${ SONG } \",\"playQueuePrime\":true} "
else
JSON = " {\"albumArtistName\":\" ${ ARTIST } \",\"albumName\":\" ${ ALBUM } \"} "
fi
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2018-06-18 17:56:30 +02:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d " ${ JSON } " \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/cloudplayer/queue-and-play?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } &mediaOwnerCustomerId= ${ MEDIAOWNERCUSTOMERID } &shuffle=false "
}
#
# play library playlist
#
play_playlist( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d " {\"playlistId\":\" ${ PLIST } \",\"playQueuePrime\":true} " \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/cloudplayer/queue-and-play?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } &mediaOwnerCustomerId= ${ MEDIAOWNERCUSTOMERID } &shuffle=false "
}
#
# play PRIME playlist
#
play_prime_playlist( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d " {\"asin\":\" ${ ASIN } \"} " \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/prime/prime-playlist-queue-and-play?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } &mediaOwnerCustomerId= ${ MEDIAOWNERCUSTOMERID } "
}
#
# play PRIME station
#
play_prime_station( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d " {\"seed\":\"{\\\"type\\\":\\\"KEY\\\",\\\"seedId\\\":\\\" ${ SEEDID } \\\"}\",\"stationName\":\"none\",\"seedType\":\"KEY\"} " \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/gotham/queue-and-play?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } &mediaOwnerCustomerId= ${ MEDIAOWNERCUSTOMERID } "
}
#
# play PRIME historical queue
#
play_prime_hist_queue( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d " {\"deviceType\":\" ${ DEVICETYPE } \",\"deviceSerialNumber\":\" ${ DEVICESERIALNUMBER } \",\"mediaOwnerCustomerId\":\" ${ MEDIAOWNERCUSTOMERID } \",\"queueId\":\" ${ HIST } \",\"service\":null,\"trackSource\":\"TRACK\"} " \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/media/play-historical-queue "
}
#
# current queue
#
show_queue( )
{
echo "/api/np/player"
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X GET \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/np/player?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } "
echo
echo "/api/np/queue"
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X GET \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/np/queue?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } "
echo
echo "/api/media/state"
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X GET \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/media/state?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } "
echo
}
#
# deletes a multiroom device
#
delete_multiroom( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X DELETE \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/lemur/tail/ ${ DEVICESERIALNUMBER } "
}
#
# creates a multiroom device
#
create_multiroom( )
{
JSON = " {\"id\":null,\"name\":\" ${ LEMUR } \",\"members\":[ "
for DEVICE in $CHILD ; do
set_var
JSON = " ${ JSON } {\"dsn\":\" ${ DEVICESERIALNUMBER } \",\"deviceType\":\" ${ DEVICETYPE } \"}, "
done
JSON = " ${ JSON %, } ]} "
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d " ${ JSON } " \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/lemur/tail "
}
2018-01-10 08:24:36 +01:00
#
# list bluetooth devices
#
list_bluetooth( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2018-01-10 08:24:36 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X GET \
" https:// ${ ALEXA } /api/bluetooth?cached=false "
}
2017-12-08 10:03:23 +01:00
#
# connect bluetooth device
#
connect_bluetooth( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d " {\"bluetoothDeviceAddress\":\" ${ BLUETOOTH } \"} " \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/bluetooth/pair-sink/ ${ DEVICETYPE } / ${ DEVICESERIALNUMBER } "
}
#
# disconnect bluetooth device
#
disconnect_bluetooth( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
2017-12-19 09:43:52 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST \
2017-12-08 10:03:23 +01:00
" https:// ${ ALEXA } /api/bluetooth/disconnect-sink/ ${ DEVICETYPE } / ${ DEVICESERIALNUMBER } "
}
2018-03-01 22:35:22 +01:00
#
# device that sent the last command
# (by Markus Wennesheimer)
#
last_alexa( )
{
${ CURL } ${ OPTS } -s -b ${ COOKIE } -A "Mozilla/5.0" -H "DNT: 1" -H "Connection: keep-alive" -L\
-H "Content-Type: application/json; charset=UTF-8" -H " Referer: https://alexa. ${ AMAZON } /spa/index.html " -H " Origin: https://alexa. ${ AMAZON } " \
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X GET \
" https:// ${ ALEXA } /api/activities?startTime=&size=1&offset=1 " | sed -r 's/^.*serialNumber":"([^"]+)".*$/\1/'
}
2017-12-08 10:03:23 +01:00
#
# logout
#
log_off( )
{
2018-01-28 18:33:16 +01:00
${ CURL } ${ OPTS } -s -c ${ COOKIE } -b ${ COOKIE } -A " ${ BROWSER } " -H "DNT: 1" -H "Connection: keep-alive" -L\
2017-12-08 10:03:23 +01:00
https://${ ALEXA } /logout > /dev/null
rm -f ${ DEVLIST }
rm -f ${ DEVTXT }
rm -f ${ COOKIE }
}
2018-03-01 22:35:22 +01:00
if [ -z " $LASTALEXA " -a -z " $BLUETOOTH " -a -z " $LEMUR " -a -z " $PLIST " -a -z " $HIST " -a -z " $SEEDID " -a -z " $ASIN " -a -z " $QUEUE " -a -z " $COMMAND " -a -z " $STATIONID " -a -z " $SONG " -a -n " $LOGOFF " ] ; then
2017-12-08 10:03:23 +01:00
echo "only logout option present, logging off ..."
log_off
exit 0
fi
if [ ! -f ${ COOKIE } ] ; then
2018-03-01 22:35:22 +01:00
echo "cookie does not exist. logging in ..."
2017-12-08 10:03:23 +01:00
log_in
fi
check_status
if [ $? -eq 0 ] ; then
echo "cookie expired, logging in again ..."
log_in
2018-01-10 08:24:36 +01:00
check_status
if [ $? -eq 0 ] ; then
echo "log in failed, aborting"
exit 1
fi
2017-12-08 10:03:23 +01:00
fi
2017-12-20 18:55:59 +01:00
if [ ! -f ${ DEVTXT } ] ; then
2018-03-01 22:35:22 +01:00
echo "device list does not exist. downloading ..."
2017-12-20 18:55:59 +01:00
get_devlist
2018-01-10 08:24:36 +01:00
if [ ! -f ${ DEVTXT } ] ; then
echo "failed to download device list, aborting"
exit 1
fi
2017-12-20 18:55:59 +01:00
fi
2017-12-08 10:03:23 +01:00
if [ -n " $COMMAND " ] ; then
set_var
if [ -n " $COMMAND " ] ; then
2018-06-18 17:56:30 +02:00
echo " sending cmd: ${ COMMAND } to dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } customerid: ${ MEDIAOWNERCUSTOMERID } "
2017-12-08 10:03:23 +01:00
run_cmd
fi
elif [ -n " $LEMUR " ] ; then
DEVICE = " ${ LEMUR } "
set_var
if [ -n " $DEVICESERIALNUMBER " ] ; then
delete_multiroom
fi
if [ -z " $CHILD " ] ; then
echo " Deleted multi room dev: ${ LEMUR } serial: ${ DEVICESERIALNUMBER } "
else
echo " Creating multi room dev: ${ LEMUR } member_dev(s): ${ CHILD } "
create_multiroom
fi
rm -f ${ DEVLIST }
rm -f ${ DEVTXT }
get_devlist
elif [ -n " $BLUETOOTH " ] ; then
set_var
if [ " $BLUETOOTH " = "null" ] ; then
echo " disconnecting dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } from bluetooth "
disconnect_bluetooth
2018-01-10 08:24:36 +01:00
elif [ " $BLUETOOTH " = "list" -o " $BLUETOOTH " = "List" -o " $BLUETOOTH " = "LIST" ] ; then
echo "bluetooth api list:"
list_bluetooth
2017-12-08 10:03:23 +01:00
else
echo " connecting dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } to bluetooth device: ${ BLUETOOTH } "
connect_bluetooth
fi
elif [ -n " $STATIONID " ] ; then
set_var
echo " playing stationID: ${ STATIONID } on dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } mediaownerid: ${ MEDIAOWNERCUSTOMERID } "
play_radio
elif [ -n " $SONG " ] ; then
set_var
echo " playing library track: ${ SONG } on dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } mediaownerid: ${ MEDIAOWNERCUSTOMERID } "
play_song
elif [ -n " $PLIST " ] ; then
set_var
echo " playing library playlist: ${ PLIST } on dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } mediaownerid: ${ MEDIAOWNERCUSTOMERID } "
play_playlist
elif [ -n " $ASIN " ] ; then
set_var
echo " playing PRIME playlist ${ ASIN } "
play_prime_playlist
elif [ -n " $SEEDID " ] ; then
set_var
echo " playing PRIME station ${ SEEDID } "
play_prime_station
elif [ -n " $HIST " ] ; then
set_var
echo " playing PRIME historical queue ${ HIST } "
play_prime_hist_queue
elif [ -n " $QUEUE " ] ; then
set_var
echo " queue info for dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } "
show_queue
elif [ -n " $LIST " ] ; then
ATTR = "accountName"
echo "the following devices exist in your account:"
grep ${ ATTR } \| ${ DEVTXT } | sed " s/^.* ${ ATTR } |// " | sed 's/ /_/g'
2018-03-01 22:35:22 +01:00
elif [ -n " $LASTALEXA " ] ; then
last_alexa
2017-12-08 10:03:23 +01:00
else
echo "no alexa command received"
fi
if [ -n " $LOGOFF " ] ; then
echo "logout option present, logging off ..."
log_off
fi