2017-10-18 19:28:30 +02:00
#!/bin/sh
#
# Amazon Alexa Remote Control
# alex(at)loetzimmer.de
#
# 2017-10-10: v0.1 initial release
# 2017-10-11: v0.2 TuneIn Station Search
# 2017-10-11: v0.2a commands on special device "ALL" are executed on all ECHO+WHA
# 2017-10-16: v0.3 added playback of library tracks
2017-12-08 10:03:23 +01:00
# 2017-10-24: v0.4 added playback information
# 2017-11-21: v0.5 added Prime station and playlist
# 2017-11-22: v0.6 added Prime historical queue and replaced getopts
# 2017-11-25: v0.6a cURL is now configurable
# 2017-11-25: v0.7 added multiroom create/delete, playback of library playlist
# 2017-11-30: v0.7a added US config, fixed device names containing spaces
# 2017-12-07: v0.7b added Bluetooth connect/disconnect
2017-12-18 09:51:10 +01:00
# 2017-12-18: v0.7c fixed US version
2017-12-19 09:43:52 +01:00
# 2017-12-19: v0.7d fixed AWK csrf extraction on some systems
2017-12-20 18:55:59 +01:00
# 2017-12-20: v0.7e moved get_devlist after check_status
2018-01-10 08:24:36 +01:00
# 2018-01-08: v0.7f added echo-show to ALL group, TuneIn station can now be up to 6 digits
# 2018-01-08: v0.8 added bluetooth list function
# 2018-01-10: v0.8a abort when login was unsuccessful
2018-01-25 10:38:42 +01:00
# 2018-01-25: v0.8b added echo-spot to ALL group
2018-01-28 18:33:16 +01:00
# 2018-01-28: v0.8c added configurable browser string
2018-03-01 22:35:22 +01:00
# 2018-02-17: v0.8d no need to write the cookie file on every "check_status"
# 2018-02-27: v0.8e added "lastalexa" option for HA-Bridge to send its command to a specific device
# (Markus Wennesheimer: https://wennez.wordpress.com/light-on-with-alexa-for-each-room/)
# 2018-02-27: v0.9 unsuccessful logins will now give a short info how to debug the login
2018-03-09 18:47:27 +01:00
# 2018-03-09: v0.9a workaround for login problem, force curl to use http1.1
2018-05-17 09:39:17 +02:00
# 2018-05-17: v0.9b update browser string and accept language
2018-06-18 17:56:30 +02:00
# 2018-05-23: v0.9c update accept language (again)
# 2018-06-12: v0.10 introducing TTS and more
# (thanks to Michael Geramb and his openHAB2 Amazon Echo Control binding)
# https://github.com/openhab/openhab2-addons/tree/master/addons/binding/org.openhab.binding.amazonechocontrol
# (thanks to Ralf Otto for implementing this feature in this script)
# 2018-06-13: v0.10a added album play of imported library
2019-01-23 00:07:58 +01:00
# 2018-06-18: v0.10b added Alexa routine execution
# 2019-01-22: v0.11 added repeat command, added environment variable parsing
2019-02-03 22:48:03 +01:00
# 2019-02-03: v0.11a fixed string escape for automation and speak commands
2019-02-10 20:04:53 +01:00
# 2019-02-10: v0.12 added "-d ALL" to the plain version, lastalexa now checks for SUCCESS activityStatus
2017-10-18 19:28:30 +02: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-10-18 19:28:30 +02:00
# - jq as command line JSON parser (optional for the fancy bits)
#
##########################################
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-02-03 22:48:03 +01:00
SET_LANGUAGE = "de,en-US;q=0.7,en;q=0.3"
2019-01-23 00:07:58 +01:00
#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'
2017-12-08 10:03:23 +01:00
2019-01-23 00:07:58 +01:00
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:52:51 +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-10-18 19:28:30 +02: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'
2019-02-10 20:04:53 +01:00
#SET_BROWSER='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0'
2019-01-23 00:07:58 +01:00
# tmp path
SET_TMP = "/tmp"
2018-01-28 18:33:16 +01:00
2017-10-18 19:28:30 +02: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 "
2017-10-18 19:28:30 +02:00
2017-12-08 10:03:23 +01:00
GUIVERSION = 0
2017-10-18 19:28:30 +02:00
LIST = ""
LOGOFF = ""
COMMAND = ""
2018-06-18 17:56:30 +02:00
TTS = ""
2018-06-18 20:27:31 +02:00
UTTERANCE = ""
2018-06-18 17:56:30 +02:00
SEQUENCECMD = ""
2017-10-18 19:28:30 +02:00
STATIONID = ""
2017-12-08 10:03:23 +01:00
QUEUE = ""
2017-10-18 19:28:30 +02:00
SONG = ""
2018-06-18 17:56:30 +02:00
ALBUM = ""
ARTIST = ""
2017-12-08 10:03:23 +01:00
TYPE = ""
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 " -i | -p | -P | -S | -a | -m <multiroom_device> [device_1 .. device_X] | -lastalexa | -l | -h"
echo
echo " -e : run command, additional SEQUENCECMDs:"
2018-06-18 20:27:31 +02:00
echo " weather,traffic,flashbriefing,goodmorning,singasong,tellstory,speak:'<text>',automation:'<routine name>'"
2018-01-10 08:24:36 +01:00
echo " -b : connect/disconnect/list bluetooth device"
2017-12-08 10:03:23 +01:00
echo " -q : query queue"
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 " -i : list imported library tracks"
echo " -p : list purchased library tracks"
echo " -P : list Prime playlists"
echo " -S : list Prime stations"
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 device 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
# stationIDs are "s1234" or "s12345"
2018-01-10 08:24:36 +01:00
if [ -n " ${ STATIONID ##s[0-9][0-9][0-9][0-9] } " -a -n " ${ STATIONID ##s[0-9][0-9][0-9][0-9][0-9] } " -a -n " ${ STATIONID ##s[0-9][0-9][0-9][0-9][0-9][0-9] } " ] ; then
2017-12-08 10:03:23 +01:00
# search for station name
STATIONID = $( ${ CURL } ${ OPTS } -s --data-urlencode " query= ${ STATIONID } " -G "https://api.tunein.com/profiles?fullTextSearch=true" | jq -r '.Items[] | select(.ContainerType == "Stations") | .Children[] | select( .Index==1 ) | .GuideId' )
if [ -z " $STATIONID " ] ; then
echo " ERROR: no Station \" $2 \" found on TuneIn "
exit 1
fi
fi
; ;
-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
; ;
-l)
LOGOFF = "true"
; ;
-a)
LIST = "true"
; ;
-i)
TYPE = "IMPORTED"
; ;
-p)
TYPE = "PURCHASES"
; ;
-P)
PRIME = "prime-playlist-browse-nodes"
; ;
-S)
PRIME = "prime-sections"
; ;
-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
2017-10-18 19:28:30 +02:00
done
case " $COMMAND " in
2017-12-08 10:03:23 +01:00
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'
2019-02-03 22:48:03 +01:00
TTS = $( echo ${ COMMAND ##* : } | sed -r 's/[^-a-zA-Z0-9_,?! ]//g' )
2018-06-18 17:56:30 +02:00
TTS = " ,\\\"textToSpeak\\\":\\\" ${ TTS } \\\" "
; ;
2018-06-18 20:27:31 +02:00
automation:*)
SEQUENCECMD = 'automation'
UTTERANCE = $( echo ${ COMMAND ##* : } | sed -r 's/[^-a-zA-Z0-9_,?! ]//g' )
; ;
2018-06-18 17:56:30 +02:00
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
; ;
2017-10-18 19:28:30 +02:00
esac
#
# Amazon Login
#
log_in( )
{
################################################################
#
# following headers are required:
2017-12-08 10:03:23 +01:00
# Accept-Language (possibly for determining login region)
# User-Agent (cURL wouldn't store cookies without)
2017-10-18 19:28:30 +02:00
#
################################################################
rm -f ${ DEVLIST }
rm -f ${ COOKIE }
2017-12-08 10:03:23 +01:00
rm -f ${ TMP } /.alexa.*.list
2017-10-18 19:28:30 +02:00
#
# 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 "
2017-10-18 19:28:30 +02:00
#
2018-03-01 22:35:22 +01:00
# login empty to generate session
2017-10-18 19:28:30 +02: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 "
2017-10-18 19:28:30 +02:00
#
# 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 "
# 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-10-18 19:28:30 +02: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 "
}
2017-10-18 19:28:30 +02:00
#
# get JSON device list
#
2017-12-08 10:03:23 +01:00
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 }
2017-10-18 19:28:30 +02:00
}
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-10-18 19:28:30 +02:00
2017-12-08 10:03:23 +01:00
if [ " $AUTHSTATUS " = "true" ] ; then
return 1
fi
2017-10-18 19:28:30 +02:00
2017-12-08 10:03:23 +01:00
return 0
2017-10-18 19:28:30 +02:00
}
#
# set device specific variables from JSON device list
#
set_var( )
{
2017-12-08 10:03:23 +01:00
DEVICE = $( echo ${ DEVICE } | sed -r 's/%20/ /g' )
2018-03-01 22:35:22 +01:00
2017-12-08 10:03:23 +01:00
if [ -z " ${ DEVICE } " ] ; then
# if no device was supplied, use the first Echo(dot) in device list
echo "setting default device to:"
2018-01-25 10:38:42 +01:00
DEVICE = $( jq -r '[ .devices[] | select(.deviceFamily == "ECHO" or .deviceFamily == "KNIGHT" or .deviceFamily == "ROOK" ) | .accountName] | .[0]' ${ DEVLIST } )
2017-12-08 10:03:23 +01:00
echo ${ DEVICE }
fi
DEVICETYPE = $( jq --arg device " ${ DEVICE } " -r '.devices[] | select(.accountName == $device) | .deviceType' ${ DEVLIST } )
DEVICESERIALNUMBER = $( jq --arg device " ${ DEVICE } " -r '.devices[] | select(.accountName == $device) | .serialNumber' ${ DEVLIST } )
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
# MEDIAOWNERCUSTOMERID=$(jq --arg device "${DEVICE}" -r '.devices[] | select(.accountName == $device) | .deviceOwnerCustomerId' ${DEVLIST})
2017-12-08 10:03:23 +01:00
if [ -z " ${ DEVICESERIALNUMBER } " ] ; then
echo " ERROR: unkown device dev: ${ DEVICE } "
exit 1
fi
2017-10-18 19:28:30 +02:00
}
#
# list available devices from JSON device list
#
list_devices( )
{
2017-12-08 10:03:23 +01:00
jq -r '.devices[].accountName' ${ DEVLIST }
2017-10-18 19:28:30 +02:00
}
#
# execute command
2018-06-18 17:56:30 +02:00
# (SequenceCommands by Michael Geramb and Ralf Otto)
2017-10-18 19:28:30 +02:00
#
run_cmd( )
{
2018-06-18 17:56:30 +02:00
if [ -n " ${ SEQUENCECMD } " ]
then
2018-06-18 20:27:31 +02:00
if [ " ${ SEQUENCECMD } " = 'automation' ] ; then
2019-02-03 22:48:03 +01:00
2018-06-18 20:27:31 +02:00
${ 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 GET \
" https:// ${ ALEXA } /api/behaviors/automations " > " ${ TMP } /.alexa.automation "
AUTOMATION = $( jq --arg utterance " ${ UTTERANCE } " -r '.[] | select( .triggers[].payload.utterance == $utterance) | .automationId' " ${ TMP } /.alexa.automation " )
if [ -z " ${ AUTOMATION } " ] ; then
echo " ERROR: no such utterance ' ${ UTTERANCE } ' in Alexa routines "
rm -f " ${ TMP } /.alexa.automation "
exit 1
fi
2019-02-03 22:48:03 +01:00
SEQUENCE = $( jq --arg utterance " ${ UTTERANCE } " -r -c '.[] | select( .triggers[].payload.utterance == $utterance) | .sequence' " ${ TMP } /.alexa.automation " | sed 's/"/\\"/g' | sed " s/ALEXA_CURRENT_DEVICE_TYPE/ ${ DEVICETYPE } /g " | sed " s/ALEXA_CURRENT_DSN/ ${ DEVICESERIALNUMBER } /g " | sed " s/ALEXA_CUSTOMER_ID/ ${ MEDIAOWNERCUSTOMERID } /g " )
2018-06-18 20:27:31 +02:00
rm -f " ${ TMP } /.alexa.automation "
echo " Running routine: ${ UTTERANCE } "
COMMAND = " {\"behaviorId\":\" ${ AUTOMATION } \",\"sequenceJson\":\" ${ SEQUENCE } \",\"status\":\"ENABLED\"} "
else
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\"} "
fi
2018-06-18 17:56:30 +02:00
2019-02-03 22:48:03 +01:00
# Due to some weird shell-escape-behavior the command has t be written to a file before POSTing it
echo $COMMAND > " ${ TMP } /.alexa.cmd "
2018-06-18 17:56:30 +02:00
${ 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 } " \
2019-02-03 22:48:03 +01:00
-H " csrf: $( awk " \$0 ~/. ${ AMAZON } .*csrf[ \\s\\t]+/ {print \$7} " ${ COOKIE } ) " -X POST -d @" ${ TMP } /.alexa.cmd " \
2018-06-18 17:56:30 +02:00
" https:// ${ ALEXA } /api/behaviors/preview "
2019-02-03 22:48:03 +01:00
rm -f " ${ TMP } /.alexa.cmd "
2018-06-18 17:56:30 +02:00
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-10-18 19:28:30 +02: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 } "
2017-10-18 19:28:30 +02:00
}
#
# 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 "
}
#
# show library tracks
#
show_library( )
{
OFFSET = "" ;
SIZE = 50;
TOTAL = 0;
FILE = ${ TMP } /.alexa.${ TYPE } .list
if [ ! -f ${ FILE } ] ; then
echo -n '{"playlist":{"entryList":[' > ${ FILE }
while [ 50 -le ${ SIZE } ] ; do
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/cloudplayer/playlists/ ${ TYPE } -V0-OBJECTID?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } &size= ${ SIZE } &offset= ${ OFFSET } &mediaOwnerCustomerId= ${ MEDIAOWNERCUSTOMERID } " > ${ FILE } .tmp
OFFSET = $( jq -r '.nextResultsToken' ${ FILE } .tmp)
SIZE = $( jq -r '.playlist | .trackCount' ${ FILE } .tmp)
jq -r -c '.playlist | .entryList' ${ FILE } .tmp >> ${ FILE }
echo "," >> ${ FILE }
TOTAL = $(( TOTAL+SIZE))
done
echo " []],\"trackCount\":\" ${ TOTAL } \"}} " >> ${ FILE }
rm -f ${ FILE } .tmp
fi
jq -r '.playlist.trackCount' ${ FILE }
jq '.playlist.entryList[] | .[]' ${ FILE }
2017-10-18 19:28:30 +02:00
}
2017-12-08 10:03:23 +01:00
#
# show Prime stations and playlists
#
show_prime( )
{
FILE = ${ TMP } /.alexa.${ PRIME } .list
if [ ! -f ${ FILE } ] ; then
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/prime/{ $PRIME }?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } &mediaOwnerCustomerId= ${ MEDIAOWNERCUSTOMERID } " > ${ FILE }
if [ " $PRIME " = "prime-playlist-browse-nodes" ] ; then
for I in $( jq -r '.primePlaylistBrowseNodeList[].subNodes[].nodeId' ${ FILE } 2>/dev/null) ; do
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/prime/prime-playlists-by-browse-node?browseNodeId= ${ I } &deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } &mediaOwnerCustomerId= ${ MEDIAOWNERCUSTOMERID } " >> ${ FILE }
done
fi
fi
jq '.' ${ FILE }
}
2017-10-18 19:28:30 +02:00
2017-12-08 10:03:23 +01:00
#
# current queue
#
show_queue( )
{
PARENT = ""
PARENTID = $( jq --arg device " ${ DEVICE } " -r '.devices[] | select(.accountName == $device) | .parentClusters[0]' ${ DEVLIST } )
if [ " $PARENTID " != "null" ] ; then
PARENTDEVICE = $( jq --arg serial ${ PARENTID } -r '.devices[] | select(.serialNumber == $serial) | .deviceType' ${ DEVLIST } )
PARENT = " &lemurId= ${ PARENTID } &lemurDeviceType= ${ PARENTDEVICE } "
fi
2018-06-18 20:27:31 +02:00
${ 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 GET \
" https:// ${ ALEXA } /api/np/player?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } ${ PARENT } " | jq '.'
${ 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 GET \
" https:// ${ ALEXA } /api/media/state?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } " | jq '.'
${ 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 GET \
" https:// ${ ALEXA } /api/np/queue?deviceSerialNumber= ${ DEVICESERIALNUMBER } &deviceType= ${ DEVICETYPE } " | jq '.'
2017-12-08 10:03:23 +01:00
}
2017-10-18 19:28:30 +02:00
2017-12-08 10:03:23 +01:00
#
# 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 " | jq --arg serial " ${ DEVICESERIALNUMBER } " -r '.bluetoothStates[] | select(.deviceSerialNumber == $serial) | "\(.pairedDeviceList[]?.address) \(.pairedDeviceList[]?.friendlyName)"'
}
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 } "
}
2017-10-18 19:28:30 +02:00
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 \
2019-02-10 20:04:53 +01:00
" https:// ${ ALEXA } /api/activities?startTime=&size=10&offset=1 " | jq -r '[.activities[] | select( .activityStatus == "SUCCESS" )][0] | .sourceDeviceIds[0].serialNumber' | xargs -i jq -r --arg device { } '.devices[] | select( .serialNumber == $device) | .accountName' ${ DEVLIST }
# Serial number: | jq -r '[.activities[] | select( .activityStatus == "SUCCESS" )][0] | .sourceDeviceIds[0].serialNumber'
# Device name: | jq -r '[.activities[] | select( .activityStatus == "SUCCESS" )][0] | .sourceDeviceIds[0].serialNumber' | xargs -i jq -r --arg device {} '.devices[] | select( .serialNumber == $device) | .accountName' ${DEVLIST}
2018-03-01 22:35:22 +01:00
}
2017-10-18 19:28:30 +02: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
2017-10-18 19:28:30 +02:00
rm -f ${ DEVLIST }
rm -f ${ COOKIE }
2017-12-08 10:03:23 +01:00
rm -f ${ TMP } /.alexa.*.list
2017-10-18 19:28:30 +02:00
}
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 " $PRIME " -a -z " $TYPE " -a -z " $QUEUE " -a -z " $LIST " -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
2017-10-18 19:28:30 +02:00
fi
check_status
if [ $? -eq 0 ] ; then
2017-12-08 10:03:23 +01:00
echo "cookie expired, logging in again ..."
log_in
2018-01-09 23:58:52 +01:00
check_status
if [ $? -eq 0 ] ; then
echo "log in failed, aborting"
exit 1
fi
2017-10-18 19:28:30 +02:00
fi
2017-12-20 18:55:59 +01:00
if [ ! -f ${ DEVLIST } ] ; 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-09 23:58:52 +01:00
if [ ! -f ${ DEVLIST } ] ; 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 " -o -n " $QUEUE " ] ; then
if [ " ${ DEVICE } " = "ALL" ] ; then
2018-01-25 10:38:42 +01:00
for DEVICE in $( jq -r '.devices[] | select( .deviceFamily == "ECHO" or .deviceFamily == "KNIGHT" or .deviceFamily == "ROOK" or .deviceFamily == "WHA") | .accountName' ${ DEVLIST } | sed -r 's/ /%20/g' ) ; do
2017-12-08 10:03:23 +01:00
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
2019-02-10 20:04:53 +01:00
# in order to prevent a "Rate exceeded" we need to delay the command
sleep 1
2017-12-08 10:03:23 +01:00
else
echo " queue info for dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } "
show_queue
fi
done
else
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
else
echo " queue info for dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } "
show_queue
fi
fi
elif [ -n " $LEMUR " ] ; then
DEVICESERIALNUMBER = $( jq --arg device " ${ LEMUR } " -r '.devices[] | select(.accountName == $device and .deviceFamily == "WHA") | .serialNumber' ${ DEVLIST } )
if [ -n " $DEVICESERIALNUMBER " ] ; then
delete_multiroom
else
if [ -z " $CHILD " ] ; then
echo " ERROR: ${ LEMUR } is no multiroom device. Cannot delete ${ LEMUR } " .
exit 1
fi
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 }
get_devlist
elif [ -n " $BLUETOOTH " ] ; then
2018-01-10 08:24:36 +01:00
if [ " $BLUETOOTH " = "list" -o " $BLUETOOTH " = "List" -o " $BLUETOOTH " = "LIST" ] ; then
if [ " ${ DEVICE } " = "ALL" ] ; then
2018-01-25 10:38:42 +01:00
for DEVICE in $( jq -r '.devices[] | select( .deviceFamily == "ECHO" or .deviceFamily == "KNIGHT" or .deviceFamily == "ROOK" or .deviceFamily == "WHA") | .accountName' ${ DEVLIST } | sed -r 's/ /%20/g' ) ; do
2018-01-10 08:24:36 +01:00
set_var
echo " bluetooth devices for dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } : "
list_bluetooth
done
else
set_var
echo " bluetooth devices for dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } : "
list_bluetooth
fi
elif [ " $BLUETOOTH " = "null" ] ; then
set_var
2017-12-08 10:03:23 +01:00
echo " disconnecting dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } from bluetooth "
disconnect_bluetooth
else
2018-01-10 08:24:36 +01:00
set_var
2017-12-08 10:03:23 +01:00
echo " connecting dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } to bluetooth device: ${ BLUETOOTH } "
connect_bluetooth
fi
2017-10-18 19:28:30 +02:00
elif [ -n " $STATIONID " ] ; then
2017-12-08 10:03:23 +01:00
set_var
echo " playing stationID: ${ STATIONID } on dev: ${ DEVICE } type: ${ DEVICETYPE } serial: ${ DEVICESERIALNUMBER } mediaownerid: ${ MEDIAOWNERCUSTOMERID } "
play_radio
2017-10-18 19:28:30 +02:00
elif [ -n " $SONG " ] ; then
2017-12-08 10:03:23 +01:00
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
2017-10-18 19:28:30 +02:00
elif [ -n " $LIST " ] ; then
2017-12-08 10:03:23 +01:00
echo "the following devices exist in your account:"
list_devices
elif [ -n " $TYPE " ] ; then
set_var
echo -n " the following songs exist in your ${ TYPE } library: "
show_library
elif [ -n " $PRIME " ] ; then
set_var
echo " the following songs exist in your PRIME ${ PRIME } : "
show_prime
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
2018-03-01 22:35:22 +01:00
elif [ -n " $LASTALEXA " ] ; then
last_alexa
2017-10-18 19:28:30 +02:00
else
2017-12-08 10:03:23 +01:00
echo "no alexa command received"
2017-10-18 19:28:30 +02:00
fi
if [ -n " $LOGOFF " ] ; then
2017-12-08 10:03:23 +01:00
echo "logout option present, logging off ..."
log_off
2017-10-18 19:28:30 +02:00
fi