mirror of
https://github.com/thorsten-gehrig/alexa-remote-control.git
synced 2024-11-14 04:49:03 +01:00
commit
33cc599f07
30
README.md
30
README.md
@ -4,17 +4,12 @@ control Amazon Alexa from command Line
|
||||
|
||||
The settings can now be controlled via environment variables.
|
||||
```
|
||||
EMAIL - your login email
|
||||
PASSWORD - your login password
|
||||
BROWSER - the User-Agent your browser sends in the request header
|
||||
LANGUAGE - the Accept-Language your browser sends in the request header
|
||||
AMAZON - your Amazon domain
|
||||
ALEXA - the URL you would use for the Alexa Web App
|
||||
CURL - location of your cURL binary
|
||||
OPTS - any cURL options you require
|
||||
TMP - location of the temp dir
|
||||
OATHTOOL - command line for oathtool MFA
|
||||
MFA_SECRET- the MFA secret
|
||||
SPEAKVOL - the volume for speak messages ( if set to 0, volume levels are left untouched)
|
||||
NORMALVOL - if no current playing volume can be determined, fall back to normal volume
|
||||
VOLMAXAGE - max. age in minutes before volume is re-read from API
|
||||
@ -22,11 +17,7 @@ DEVICEVOLNAME - a list of device names with specific volume settings (space se
|
||||
DEVICEVOLSPEAK - a list of speak volume levels - matching the devices above
|
||||
DEVICEVOLNORMAL - a list of normal volume levels- matching the devices above
|
||||
(current playing volume takes precedence for normal volume)
|
||||
REFRESH_TOKEN - the new preference over EMAIL/PASSWORD can be obtained here: https://github.com/adn77/alexa-cookie-cli
|
||||
```
|
||||
You will very likely want to set the language to:
|
||||
```
|
||||
export LANGUAGE='de,en-US;q=0.7,en;q=0.3'
|
||||
REFRESH_TOKEN - the new preference over EMAIL/PASSWORD can be obtained here: https://github.com/adn77/alexa-cookie-cli
|
||||
```
|
||||
|
||||
```
|
||||
@ -66,23 +57,8 @@ alexa-remote-control [-d <device>|ALL] -e <pause|play|next|prev|fwd|rwd|shuffle|
|
||||
-h : help
|
||||
```
|
||||
|
||||
There's also a (NOW DEPRECATED) "plain" version, which lacks some functionality (-z, -i, -p, -P, -S and no radio station names and no routines) but doesn't require 'jq' for JSON processing.
|
||||
|
||||
Old option MFA
|
||||
Login via REFRESH_TOKEN
|
||||
----
|
||||
In order to use MFA, one needs to obtain the MFA_SECRET from Amazon account:
|
||||
1. You should have MFA using an App already working before proceeding
|
||||
1. Add a new app
|
||||
1. When presented with the QR-code select "can't scan code"
|
||||
1. You will be presented with the MFA shared secret, something like `1234 5678 9ABC DEFG HIJK LMNO PQRS TUVW XYZ0 1234 5678 9ABC DEFG`
|
||||
1. Now you have to generate a valid response code via `oathtool -b --totp "<MFA shared secret from above>"` and enter that in the web form
|
||||
1. Going from here the MFA shared secret becomes the MFA_SECRET for the alexa_remote_control script;
|
||||
*Treat that MFA_SECRET just like your password - DO NOT share it anywhere!!!*
|
||||
|
||||
It is assumed that MFA secured accounts are less likely to get a captcha response during login - that's why MFA might yield better results if the plain username/password didn't work for you.
|
||||
|
||||
New option REFRESH_TOKEN
|
||||
----
|
||||
The Alexa-App way of logging in is using a REFRESH_TOKEN which allows for obtaining the session cookies. This replaces EMAIL/PASSWORD/MFA so those will not be exposed in any scripts anymore. For convinience I created a binary, ready to run: https://github.com/adn77/alexa-cookie-cli
|
||||
The Alexa-App way of logging in is using a REFRESH_TOKEN which allows for obtaining the session cookies. This replaces EMAIL/PASSWORD/MFA so those will not be exposed in any scripts anymore. For convenience I created a binary, ready to run: https://github.com/adn77/alexa-cookie-cli
|
||||
|
||||
https://blog.loetzimmer.de/2021/09/alexa-remote-control-shell-script.html
|
||||
|
@ -79,6 +79,9 @@
|
||||
# -lastalexa now returns this string. Make sure to put the device in double quotes!
|
||||
# 2022-02-04: v0.20d minor volume fix (write volume to volume cache when volume is changed)
|
||||
# 2022-06-29: v0.20e removed call to jq's strptime function, replaced with bash function using 'date' to convert to epoch
|
||||
# 2024-01-29: v0.21 removed legacy login methods as they were no longer working
|
||||
# implemented new API calls for -lastalexa and -lastcommand
|
||||
# there is now an OS-type switch that hopefully handles OSX and BSD date creation
|
||||
#
|
||||
###
|
||||
#
|
||||
@ -87,23 +90,13 @@
|
||||
# - (GNU) sed and awk for extraction
|
||||
# - jq as command line JSON parser (optional for the fancy bits)
|
||||
# - base64 for B64 encoding (make sure "-w 0" option is available on your platform)
|
||||
# - oathtool as OATH one-time password tool (optional for two-factor authentication)
|
||||
#
|
||||
##########################################
|
||||
|
||||
SET_EMAIL='amazon_account@email.address'
|
||||
SET_PASSWORD='Very_Secret_Amazon_Account_Password'
|
||||
SET_MFA_SECRET=''
|
||||
# something like:
|
||||
# 1234 5678 9ABC DEFG HIJK LMNO PQRS TUVW XYZ0 1234 5678 9ABC DEFG
|
||||
|
||||
# this can be obtained by doing the device registration login flow
|
||||
# e.g. from here: https://github.com/Apollon77/alexa-cookie/
|
||||
# e.g. from here: https://github.com/adn77/alexa-cookie-cli/
|
||||
SET_REFRESH_TOKEN=''
|
||||
|
||||
SET_LANGUAGE='de,en-US;q=0.7,en;q=0.3'
|
||||
#SET_LANGUAGE='en-US'
|
||||
|
||||
SET_TTS_LOCALE='de-DE'
|
||||
|
||||
SET_AMAZON='amazon.de'
|
||||
@ -126,9 +119,6 @@ SET_OPTS='--compressed --http1.1'
|
||||
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:65.0) Gecko/20100101 Firefox/65.0'
|
||||
|
||||
# oathtool command line tool
|
||||
SET_OATHTOOL='/usr/bin/oathtool'
|
||||
|
||||
# jq binary
|
||||
SET_JQ='/usr/bin/jq'
|
||||
|
||||
@ -156,19 +146,14 @@ SET_VOLMAXAGE="1"
|
||||
#
|
||||
|
||||
# retrieving environment variables if any are set
|
||||
EMAIL=${EMAIL:-$SET_EMAIL}
|
||||
PASSWORD=${PASSWORD:-$SET_PASSWORD}
|
||||
MFA_SECRET=${MFA_SECRET:-$SET_MFA_SECRET}
|
||||
REFRESH_TOKEN=${REFRESH_TOKEN:-$SET_REFRESH_TOKEN}
|
||||
AMAZON=${AMAZON:-$SET_AMAZON}
|
||||
ALEXA=${ALEXA:-$SET_ALEXA}
|
||||
LANGUAGE=${LANGUAGE:-$SET_LANGUAGE}
|
||||
BROWSER=${BROWSER:-$SET_BROWSER}
|
||||
CURL=${CURL:-$SET_CURL}
|
||||
OPTS=${OPTS:-$SET_OPTS}
|
||||
TTS_LOCALE=${TTS_LOCALE:-$SET_TTS_LOCALE}
|
||||
TMP=${TMP:-$SET_TMP}
|
||||
OATHTOOL=${OATHTOOL:-$SET_OATHTOOL}
|
||||
JQ=${JQ:-$SET_JQ}
|
||||
SPEAKVOL=${SPEAKVOL:-$SET_SPEAKVOL}
|
||||
NORMALVOL=${NORMALVOL:-$SET_NORMALVOL}
|
||||
@ -249,7 +234,7 @@ usage()
|
||||
while [ "$#" -gt 0 ] ; do
|
||||
case "$1" in
|
||||
--version)
|
||||
echo "v0.20d"
|
||||
echo "v0.21"
|
||||
exit 0
|
||||
;;
|
||||
-d)
|
||||
@ -506,87 +491,44 @@ esac
|
||||
#
|
||||
log_in()
|
||||
{
|
||||
################################################################
|
||||
#
|
||||
# following headers are required:
|
||||
# Accept-Language (possibly for determining login region)
|
||||
# User-Agent (cURL wouldn't store cookies without)
|
||||
#
|
||||
################################################################
|
||||
|
||||
rm -f ${DEVLIST}.json
|
||||
rm -f ${COOKIE}
|
||||
rm -f ${TMP}/.alexa.*.list
|
||||
|
||||
if [ -z "${REFRESH_TOKEN}" ] ; then
|
||||
#
|
||||
# get first cookie and write redirection target into referer
|
||||
#
|
||||
${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\
|
||||
https://alexa.${AMAZON} | grep "hidden" | sed 's/hidden/\n/g' | grep "value=\"" | sed -r 's/^.*name="([^"]+)".*value="([^"]+)".*/\1=\2\&/g' > "${TMP}/.alexa.postdata"
|
||||
|
||||
#
|
||||
# login empty to generate session
|
||||
#
|
||||
${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\
|
||||
-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"
|
||||
|
||||
#
|
||||
# add OTP if using MFA
|
||||
#
|
||||
if [ -n "${MFA_SECRET}" ] ; then
|
||||
OTP=$(${OATHTOOL} -b --totp "${MFA_SECRET}")
|
||||
PASSWORD="${PASSWORD}${OTP}"
|
||||
fi
|
||||
|
||||
#
|
||||
# login with filled out form
|
||||
# !!! referer now contains session in URL
|
||||
#
|
||||
${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)"
|
||||
echo
|
||||
echo " To avoid issues with captcha, try using Multi-Factor Authentication."
|
||||
echo " To do so, first set up Two-Step Verification on your Amazon account, then"
|
||||
echo " configure this script (or the environment) with your MFA secret."
|
||||
echo " Support for Multi-Factor Authentication requires 'oathtool' to be installed."
|
||||
|
||||
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
|
||||
|
||||
rm -f "${TMP}/.alexa.login"
|
||||
rm -f "${TMP}/.alexa.header"
|
||||
rm -f "${TMP}/.alexa.header2"
|
||||
rm -f "${TMP}/.alexa.postdata"
|
||||
rm -f "${TMP}/.alexa.postdata2"
|
||||
echo "Sorry, the very thing this project started with, namely the reverse engineered"
|
||||
echo " login to the Amazon web page does no longer work. The Alexa login page has"
|
||||
echo " been shut down in favor of a much more modern login process."
|
||||
echo
|
||||
echo "Please use the device login process https://github.com/adn77/alexa-cookie-cli"
|
||||
echo " all you need is the 'refreshToken' looking sth. like 'Atnr|...'"
|
||||
else
|
||||
# ${CURL} ${OPTS} -s -X POST --data "app_name=Amazon%20Alexa&requested_token_type=auth_cookies&domain=www.${AMAZON}&source_token_type=refresh_token" --data-urlencode "source_token=${REFRESH_TOKEN}" -H "x-amzn-identity-auth-domain: api.${AMAZON}" https://api.${AMAZON}/ap/exchangetoken/cookies | ${JQ} -r '.response.tokens.cookies | to_entries[] | .key as $domain | .value[] | map_values(if . == true then "TRUE" elif . == false then "FALSE" else . end) | .Expires |= ( strptime("%d %b %Y %H:%M:%S %Z") | mktime ) | [(if .HttpOnly=="TRUE" then ("#HttpOnly_" + $domain) else $domain end), "TRUE", .Path, .Secure, .Expires, .Name, .Value] | @tsv' > ${COOKIE}
|
||||
|
||||
BSD=$(uname | tr '[:upper:]' '[:lower:]' | grep -E 'darwin|bsd')
|
||||
|
||||
# workaround for cookies valid beyond 2038-01-19 on 32-bit systems
|
||||
toEpoch() {
|
||||
local x
|
||||
while read x
|
||||
do
|
||||
echo "$x" | awk '{
|
||||
if ($3 >= 2038) {
|
||||
print "s/"$1" "$2" "$3" "$4" "$5"/2147483647/g"
|
||||
} else {
|
||||
print "s/"$1" "$2" "$3" "$4" "$5"/'"$(date -d "$x" -u +"%s")"'/g"
|
||||
}
|
||||
}'
|
||||
if [ -n "${BSD}" ] ; then
|
||||
echo "$x" | awk '{
|
||||
if ($3 >= 2038) {
|
||||
print "s/"$1" "$2" "$3" "$4" "$5"/2147483647/g"
|
||||
} else {
|
||||
print "s/"$1" "$2" "$3" "$4" "$5"/'"$(date -j -f "%d %b %Y %H:%M:%S %Z" "$x" +"%s")"'/g"
|
||||
}
|
||||
}'
|
||||
else
|
||||
echo "$x" | awk '{
|
||||
if ($3 >= 2038) {
|
||||
print "s/"$1" "$2" "$3" "$4" "$5"/2147483647/g"
|
||||
} else {
|
||||
print "s/"$1" "$2" "$3" "$4" "$5"/'"$(date -d "$x" -u +"%s")"'/g"
|
||||
}
|
||||
}'
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
@ -1189,34 +1131,65 @@ ${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep
|
||||
"https://${ALEXA}/api/bluetooth/disconnect-sink/${DEVICETYPE}/${DEVICESERIALNUMBER}"
|
||||
}
|
||||
|
||||
#
|
||||
# get activity CSRF token
|
||||
#
|
||||
get_activity_csrf()
|
||||
{
|
||||
${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\
|
||||
-H "Content-Type: application/json; charset=UTF-8" \
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X GET\
|
||||
"https://www.${AMAZON}/alexa-privacy/apd/activity?ref=activityHistory" | grep 'meta name="csrf-token" content="' | sed -r 's/^.*content="([^"]+)".*$/\1/g' > ${TMP}/.alexa.activity.csrf
|
||||
}
|
||||
|
||||
#
|
||||
# get customer history records
|
||||
#
|
||||
get_history()
|
||||
{
|
||||
if ! [ -f ${TMP}/.alexa.activity.csrf ] ; then
|
||||
get_activity_csrf
|
||||
fi
|
||||
|
||||
RES=$(${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L -w "%{http_code}" \
|
||||
-H "Content-Type: application/json; charset=UTF-8" -H "anti-csrftoken-a2z: $(cat ${TMP}/.alexa.activity.csrf)" \
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X POST -d '{"previousRequestToken": null}'\
|
||||
"https://www.${AMAZON}/alexa-privacy/apd/rvh/customer-history-records-v2/?startTime=0&endTime=2147483647000&pageType=VOICE_HISTORY" -o ${TMP}/.alexa.activity.json)
|
||||
|
||||
# try again in case CSRF timed out
|
||||
if [ $RES -ne 200 ] ; then
|
||||
if [ -z "${try}" ] ; then
|
||||
try=1
|
||||
rm -f ${TMP}/.alexa.activity.csrf
|
||||
get_history
|
||||
else
|
||||
echo "ERROR: unable to retrieve customer history records"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# device that sent the last command
|
||||
# (by Markus Wennesheimer)
|
||||
#
|
||||
last_alexa()
|
||||
{
|
||||
${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/activities?startTime=&size=10&offset=1" | ${JQ} -r '[.activities[] | select( .activityStatus == "SUCCESS" )][0] | .sourceDeviceIds[0].serialNumber' | xargs -i grep -m 1 {} ${DEVLIST}.txt
|
||||
get_history
|
||||
${JQ} -r '.customerHistoryRecords | sort_by(.timestamp) | reverse | .[0] | .device.deviceName' ${TMP}/.alexa.activity.json
|
||||
}
|
||||
|
||||
#
|
||||
# last command or last command of a specific device
|
||||
# (by Trinitus01)
|
||||
#
|
||||
last_command()
|
||||
{
|
||||
SERIALNUMBER=$(${JQ} -r --arg device "$DEVICE" '.devices[] | select( .accountName == $device ) | .serialNumber' ${DEVLIST}.json)
|
||||
ACTIVITIES=$(${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/activities?startTime=&size=10&offset=1")
|
||||
if [ -z "$DEVICE" ] ; then
|
||||
echo "$ACTIVITIES" | ${JQ} -r '[.activities[] | select( .activityStatus == "SUCCESS" )][0] | .description' | ${JQ} -r .summary
|
||||
else
|
||||
echo "$ACTIVITIES" | ${JQ} -r --arg serialnumber "$SERIALNUMBER" '[.activities[] | select( .activityStatus == "SUCCESS" ) | select( .sourceDeviceIds[].serialNumber == $serialnumber)][0] | .description' | ${JQ} -r .summary
|
||||
fi
|
||||
get_history
|
||||
|
||||
if [ -z "$DEVICE" ] ; then
|
||||
${JQ} -r --arg device "$DEVICE" '.customerHistoryRecords | sort_by(.timestamp) | reverse | .[0] | .voiceHistoryRecordItems | map({key: .recordItemType, value: .transcriptText})' ${TMP}/.alexa.activity.json
|
||||
else
|
||||
${JQ} -r --arg device "$DEVICE" '[ .customerHistoryRecords | sort_by(.timestamp) | reverse | .[] | select( .device.deviceName == $device) ][0] | .voiceHistoryRecordItems | map({key: .recordItemType, value: .transcriptText})' ${TMP}/.alexa.activity.json
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user