diff --git a/README.md b/README.md index e55fc85..d90837c 100644 --- a/README.md +++ b/README.md @@ -1 +1,37 @@ -see my branch adn77-ccokie-automation (https://github.com/adn77/alexa-remote-control/tree/adn77-cookie-automation) for the latest version + +# alexa-remote-control +control Amazon Alexa from command Line + +``` +alexa-remote-control [-d |ALL] -e > | + -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -r <\"station name\"|stationid> | + -s | -t | -u | -v | -w | + -i | -p | -a | -P | -S | -m [device_1 .. device_X] | -l | -h + -e : run command + -q : query queue + -b : connect/disconnect/list bluetooth device + -r : play tunein radio + -s : play library track + -t : play Prime playlist + -u : play Prime station + -v : play Prime historical queue + -w : play library playlist + -i : list imported library tracks + -p : list purchased library tracks + -P : list Prime playlists + -S : list Prime stations + -a : list available devices + -m : delete multiroom and/or create new multiroom containing devices + -l : logoff + -h : help +``` + +There's also a "plain" version, which lacks some functionality (-i, -p, -P, -S and no radio station names) but doesn't require 'jq' for JSON processing. + +http://blog.loetzimmer.de/2017/10/amazon-alexa-hort-auf-die-shell-echo.html + + + + + + diff --git a/alexa-pause.sh b/alexa-pause.sh deleted file mode 100644 index 2980768..0000000 --- a/alexa-pause.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -DEVICENUMBER='G090LF1234540B82' -DEVICETYPE='A3S5123456VAYF' -COOKIE='Cookie: x-amzn-dat-gui-client-v=1.24.986.0; aws-target-static-id=14712345678-839972; aws-session-id=168-0412369-4130549; aws-session-id-time=2106194071l; aws-x-main=PgpqvER9Wibauhf4zfSDriNBlBILfMnk; aws-at-main=Atza|IwEBIAFsntUWFc_PZvFde5drDzPNXXXXXXXXXHPqI53aQNl-Fu83eAMUsFJmMtamv_4mLKEh6N82lqq8OHA8SKgh44X2MZwTguXNcUvd0-RVQyayT2lQ86JjoXvJrlMlrZIs7kJLWBryCGJEXXXXXXXXXXFJQpHhnGeC72ljNT_nVObL_wZoYhMFHVMXGiYjFyrb-QcjYY-I3ar-Q-kLAmzZYQIKU-mZ8GChNefKuG_x4ZZ2A06XXXXXXXXXOhX9w94vgmn2y3Qr9Q0rEi8K--UoFuTQ54GArubNsofNLh4U2dov8D7Gs3GoW6Hp2zEwt7cyumpRR_vceAz3aUjskoNKXsgDfpPZrebl-0R4bC6BfIasyoDOX9XXXXXXXXjRihKSxf4gs79p1ExYBmaBOX6glBrsKRqOXfk6Vh87_96uQ; aws-userInfo=%7B%22arn%22%3A%22arn%3Aaws%3Aiam%3A%3A314911114493%3Aroot%22%2C%22alias%22%3A%22%22%2C%22username%22%3A%22MeinUser%2520Name%22%2C%22keybase%22%3A%22dhVv%2Fmb%2BJyCH%2BJA6zwfighlgs5jc0W0mVYXkLBkb77M%5Cu003d%22%2C%22issuer%22%3A%22https%3A%2F%2Fwww.amazon.com%2Fap%2Fsignin%22%7D; __utmv=191231197.PgpqvER9Wibauhf4zfSDXXXXXXXXk; s_dslv=1475471239073; regStatus=registered; aws-target-visitor-id=1475445653051-715340.26_4; aws-target-data=%7B%22support%22%3A%221%22%7D; s_pers=%20s_ev15%3D%255B%255B%2527Typed%252FBookmarked%2527%252C%25212375474893114%2527%255D%255D%7C1632141293114%3B%20s_fid%3D0523A63XXXXXXXXX3C9679E640DA3570%7C1538547024210%3B%20s_dl%3D1%7C1475481765932%3B%20gpv_page%3Dno%2520value%7C1475481765939%3B; session-id=151-3512347-8293901; session-id-time=2106212731l; s_fid=71689F1234D6D238-299FD69294EFC3C1; s_nr=1475492764173-Repeat; aws-ubid-main=151-3388123-1263140; aws-session-token="lprp9YroXXXXXXXXXz5EFfSaPt96R0oSBWL2dR/AGFdlmk4QwRcVPXXXXXXXrmZ5kfHBdNwvSWWMKTnYU0pB5CE2XXXXXXUk6SIuaQYti17ZQ8B3NGwyCKNs7Lil+kz9qonZS5/cwtmrWHCe7SGx/QmPXXXXXXX89WbiV2xwTRkhVw6hTjO+8WUOXXXXXXXXi8MaR196v+Ewflx7dd3ouTVtY+hbJE4XSs="; __utma=194891197.1975555581.1444474071.1475495555.1475555580.5; __utmz=194555597.1475555680.5.2.utmccn=(referral)|utmcsr=console.aws.amazon.com|utmcct=/cloudwatch/home|utmcmd=referral; ubid-main=152-8362434-2710144; session-token="Y2Eoqs81+wIbanI7mPonK9t7gFvH7ZdTG2OxCZB3orqTILNY5ev4cFBWiSPTl2u8nFNXNmwtFP6RxnPBTunkdbHfpos8WYYOX7gEF52wnn5J3SknsvO99+MAT5GeyMR4JRa+ZOX+DcnIt8xtgYY2sSaEZ3JspjrQF+M9PUkuT9D4aYbZGRNa1BkFvoOt8Kettr9xxS/ODPfXEaE6xJr7qyPv0IQ3K6e+FbfGk3AWypY="; x-main=4r7ZrJJV4BT1R2sWn2W4NpiXZm9RSJLD; at-main=Atza|IwEEEEE4EikDns-CTHnGRqXXXcnnaij75AfcF1-XOBvphYTp_ATJHu6f0vDgMI7B1ApWn51cP6bqvG-XXXXeTPD8t7jl5El_FdhlYn3mmtn476FfnI5E7anoZNSKLyE-jauNJssBeGUjsSd0z8746mujYcJbKQsMMVVUlxcu4jXXxXXYcC4GasQyvOIp92wY-TPKAOys8NV0-yXH4xTGBzgJ0zzJa5JvyRz2v0x3fijvyXXXXXXSDhZtaDOZ6GOQlYrsUmudsorYUcUT52x0EYavXXXXX92cmgkMUzuTxppV1SpLk2Wk63LQQkCCZLs6_HWlnX2Saf0zgc13BdUehhSBiYHzJJeg1Ys7g68GLG5IuSGvyGe8ebmcN9KUDdD2CwphaeW34iy6dVKGbUpJ3GKVlz; sess-at-main="eSHstcxm5Krki5T1EAcQyk9XXXXXXqMbqcJqDBaA="; csrf=-2012345694' -CSRF='csrf: -2081234594' - -CMD='{"type":"PauseCommand"}' -URL='https://pitangui.amazon.com/api/np/command?deviceSerialNumber='$DEVICENUMBER'&deviceType='$DEVICETYPE -curl $URL \ --H "$COOKIE" \ --H "$CSRF" \ ---data "${CMD}" \ ---compressed diff --git a/alexa-tunein.sh b/alexa-tunein.sh deleted file mode 100644 index e37ab8a..0000000 --- a/alexa-tunein.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -DEVICENUMBER='G090LF0912345B82' -DEVICETYPE='A3S5B12345VAYF' -CUSTOMERID='A2C7I1234559PS' -COOKIE='Cookie: x-amzn-dat-gui-client-v=1.24.986.0; aws-target-static-id=14712345678-839972; aws-session-id=168-0412369-4130549; aws-session-id-time=2106194071l; aws-x-main=PgpqvER9Wibauhf4zfSDriNBlBILfMnk; aws-at-main=Atza|IwEBIAFsntUWFc_PZvFde5drDzPNXXXXXXXXXHPqI53aQNl-Fu83eAMUsFJmMtamv_4mLKEh6N82lqq8OHA8SKgh44X2MZwTguXNcUvd0-RVQyayT2lQ86JjoXvJrlMlrZIs7kJLWBryCGJEXXXXXXXXXXFJQpHhnGeC72ljNT_nVObL_wZoYhMFHVMXGiYjFyrb-QcjYY-I3ar-Q-kLAmzZYQIKU-mZ8GChNefKuG_x4ZZ2A06XXXXXXXXXOhX9w94vgmn2y3Qr9Q0rEi8K--UoFuTQ54GArubNsofNLh4U2dov8D7Gs3GoW6Hp2zEwt7cyumpRR_vceAz3aUjskoNKXsgDfpPZrebl-0R4bC6BfIasyoDOX9XXXXXXXXjRihKSxf4gs79p1ExYBmaBOX6glBrsKRqOXfk6Vh87_96uQ; aws-userInfo=%7B%22arn%22%3A%22arn%3Aaws%3Aiam%3A%3A314911114493%3Aroot%22%2C%22alias%22%3A%22%22%2C%22username%22%3A%22MeinUser%2520Name%22%2C%22keybase%22%3A%22dhVv%2Fmb%2BJyCH%2BJA6zwfighlgs5jc0W0mVYXkLBkb77M%5Cu003d%22%2C%22issuer%22%3A%22https%3A%2F%2Fwww.amazon.com%2Fap%2Fsignin%22%7D; __utmv=191231197.PgpqvER9Wibauhf4zfSDXXXXXXXXk; s_dslv=1475471239073; regStatus=registered; aws-target-visitor-id=1475445653051-715340.26_4; aws-target-data=%7B%22support%22%3A%221%22%7D; s_pers=%20s_ev15%3D%255B%255B%2527Typed%252FBookmarked%2527%252C%25212375474893114%2527%255D%255D%7C1632141293114%3B%20s_fid%3D0523A63XXXXXXXXX3C9679E640DA3570%7C1538547024210%3B%20s_dl%3D1%7C1475481765932%3B%20gpv_page%3Dno%2520value%7C1475481765939%3B; session-id=151-3512347-8293901; session-id-time=2106212731l; s_fid=71689F1234D6D238-299FD69294EFC3C1; s_nr=1475492764173-Repeat; aws-ubid-main=151-3388123-1263140; aws-session-token="lprp9YroXXXXXXXXXz5EFfSaPt96R0oSBWL2dR/AGFdlmk4QwRcVPXXXXXXXrmZ5kfHBdNwvSWWMKTnYU0pB5CE2XXXXXXUk6SIuaQYti17ZQ8B3NGwyCKNs7Lil+kz9qonZS5/cwtmrWHCe7SGx/QmPXXXXXXX89WbiV2xwTRkhVw6hTjO+8WUOXXXXXXXXi8MaR196v+Ewflx7dd3ouTVtY+hbJE4XSs="; __utma=194891197.1975555581.1444474071.1475495555.1475555580.5; __utmz=194555597.1475555680.5.2.utmccn=(referral)|utmcsr=console.aws.amazon.com|utmcct=/cloudwatch/home|utmcmd=referral; ubid-main=152-8362434-2710144; session-token="Y2Eoqs81+wIbanI7mPonK9t7gFvH7ZdTG2OxCZB3orqTILNY5ev4cFBWiSPTl2u8nFNXNmwtFP6RxnPBTunkdbHfpos8WYYOX7gEF52wnn5J3SknsvO99+MAT5GeyMR4JRa+ZOX+DcnIt8xtgYY2sSaEZ3JspjrQF+M9PUkuT9D4aYbZGRNa1BkFvoOt8Kettr9xxS/ODPfXEaE6xJr7qyPv0IQ3K6e+FbfGk3AWypY="; x-main=4r7ZrJJV4BT1R2sWn2W4NpiXZm9RSJLD; at-main=Atza|IwEEEEE4EikDns-CTHnGRqXXXcnnaij75AfcF1-XOBvphYTp_ATJHu6f0vDgMI7B1ApWn51cP6bqvG-XXXXeTPD8t7jl5El_FdhlYn3mmtn476FfnI5E7anoZNSKLyE-jauNJssBeGUjsSd0z8746mujYcJbKQsMMVVUlxcu4jXXxXXYcC4GasQyvOIp92wY-TPKAOys8NV0-yXH4xTGBzgJ0zzJa5JvyRz2v0x3fijvyXXXXXXSDhZtaDOZ6GOQlYrsUmudsorYUcUT52x0EYavXXXXX92cmgkMUzuTxppV1SpLk2Wk63LQQkCCZLs6_HWlnX2Saf0zgc13BdUehhSBiYHzJJeg1Ys7g68GLG5IuSGvyGe8ebmcN9KUDdD2CwphaeW34iy6dVKGbUpJ3GKVlz; sess-at-main="eSHstcxm5Krki5T1EAcQyk9XXXXXXqMbqcJqDBaA="; csrf=-2012345694' -CSRF='csrf: -2081234594' - -STATION=$1 - -#Check if parameter is set -if [ -z "$STATION" ]; -then - echo "Station Empty - ./alexa-tunein.sh s16430 " - exit -fi -#echo "${STATION}" - -URL='https://pitangui.amazon.com/api/tunein/queue-and-play?deviceSerialNumber='$DEVICENUMBER'&deviceType='$DEVICETYPE'&guideId='$STATION'&contentType=station&callSign=&mediaOwnerCustomerId='$CUSTOMERID -curl $URL \ --X POST \ --H "$CSRF" \ --H "$COOKIE" \ ---compressed - diff --git a/alexa-volume.sh b/alexa-volume.sh deleted file mode 100644 index 43269fa..0000000 --- a/alexa-volume.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -DEVICENUMBER='G090LF1234540B82' -DEVICETYPE='A3S5123456VAYF' -COOKIE='Cookie: x-amzn-dat-gui-client-v=1.24.986.0; aws-target-static-id=14712345678-839972; aws-session-id=168-0412369-4130549; aws-session-id-time=2106194071l; aws-x-main=PgpqvER9Wibauhf4zfSDriNBlBILfMnk; aws-at-main=Atza|IwEBIAFsntUWFc_PZvFde5drDzPNXXXXXXXXXHPqI53aQNl-Fu83eAMUsFJmMtamv_4mLKEh6N82lqq8OHA8SKgh44X2MZwTguXNcUvd0-RVQyayT2lQ86JjoXvJrlMlrZIs7kJLWBryCGJEXXXXXXXXXXFJQpHhnGeC72ljNT_nVObL_wZoYhMFHVMXGiYjFyrb-QcjYY-I3ar-Q-kLAmzZYQIKU-mZ8GChNefKuG_x4ZZ2A06XXXXXXXXXOhX9w94vgmn2y3Qr9Q0rEi8K--UoFuTQ54GArubNsofNLh4U2dov8D7Gs3GoW6Hp2zEwt7cyumpRR_vceAz3aUjskoNKXsgDfpPZrebl-0R4bC6BfIasyoDOX9XXXXXXXXjRihKSxf4gs79p1ExYBmaBOX6glBrsKRqOXfk6Vh87_96uQ; aws-userInfo=%7B%22arn%22%3A%22arn%3Aaws%3Aiam%3A%3A314911114493%3Aroot%22%2C%22alias%22%3A%22%22%2C%22username%22%3A%22MeinUser%2520Name%22%2C%22keybase%22%3A%22dhVv%2Fmb%2BJyCH%2BJA6zwfighlgs5jc0W0mVYXkLBkb77M%5Cu003d%22%2C%22issuer%22%3A%22https%3A%2F%2Fwww.amazon.com%2Fap%2Fsignin%22%7D; __utmv=191231197.PgpqvER9Wibauhf4zfSDXXXXXXXXk; s_dslv=1475471239073; regStatus=registered; aws-target-visitor-id=1475445653051-715340.26_4; aws-target-data=%7B%22support%22%3A%221%22%7D; s_pers=%20s_ev15%3D%255B%255B%2527Typed%252FBookmarked%2527%252C%25212375474893114%2527%255D%255D%7C1632141293114%3B%20s_fid%3D0523A63XXXXXXXXX3C9679E640DA3570%7C1538547024210%3B%20s_dl%3D1%7C1475481765932%3B%20gpv_page%3Dno%2520value%7C1475481765939%3B; session-id=151-3512347-8293901; session-id-time=2106212731l; s_fid=71689F1234D6D238-299FD69294EFC3C1; s_nr=1475492764173-Repeat; aws-ubid-main=151-3388123-1263140; aws-session-token="lprp9YroXXXXXXXXXz5EFfSaPt96R0oSBWL2dR/AGFdlmk4QwRcVPXXXXXXXrmZ5kfHBdNwvSWWMKTnYU0pB5CE2XXXXXXUk6SIuaQYti17ZQ8B3NGwyCKNs7Lil+kz9qonZS5/cwtmrWHCe7SGx/QmPXXXXXXX89WbiV2xwTRkhVw6hTjO+8WUOXXXXXXXXi8MaR196v+Ewflx7dd3ouTVtY+hbJE4XSs="; __utma=194891197.1975555581.1444474071.1475495555.1475555580.5; __utmz=194555597.1475555680.5.2.utmccn=(referral)|utmcsr=console.aws.amazon.com|utmcct=/cloudwatch/home|utmcmd=referral; ubid-main=152-8362434-2710144; session-token="Y2Eoqs81+wIbanI7mPonK9t7gFvH7ZdTG2OxCZB3orqTILNY5ev4cFBWiSPTl2u8nFNXNmwtFP6RxnPBTunkdbHfpos8WYYOX7gEF52wnn5J3SknsvO99+MAT5GeyMR4JRa+ZOX+DcnIt8xtgYY2sSaEZ3JspjrQF+M9PUkuT9D4aYbZGRNa1BkFvoOt8Kettr9xxS/ODPfXEaE6xJr7qyPv0IQ3K6e+FbfGk3AWypY="; x-main=4r7ZrJJV4BT1R2sWn2W4NpiXZm9RSJLD; at-main=Atza|IwEEEEE4EikDns-CTHnGRqXXXcnnaij75AfcF1-XOBvphYTp_ATJHu6f0vDgMI7B1ApWn51cP6bqvG-XXXXeTPD8t7jl5El_FdhlYn3mmtn476FfnI5E7anoZNSKLyE-jauNJssBeGUjsSd0z8746mujYcJbKQsMMVVUlxcu4jXXxXXYcC4GasQyvOIp92wY-TPKAOys8NV0-yXH4xTGBzgJ0zzJa5JvyRz2v0x3fijvyXXXXXXSDhZtaDOZ6GOQlYrsUmudsorYUcUT52x0EYavXXXXX92cmgkMUzuTxppV1SpLk2Wk63LQQkCCZLs6_HWlnX2Saf0zgc13BdUehhSBiYHzJJeg1Ys7g68GLG5IuSGvyGe8ebmcN9KUDdD2CwphaeW34iy6dVKGbUpJ3GKVlz; sess-at-main="eSHstcxm5Krki5T1EAcQyk9XXXXXXqMbqcJqDBaA="; csrf=-2012345694' -CSRF='csrf: -2081234594' - -VOLUME=$1 - -#check if parameter is set -if [ -z "$VOLUME" ]; -then - echo "use ./alexa-volume.sh 40 # Use something between 0-100" - exit -fi - -VOLUMECMD='{"type":"VolumeLevelCommand","volumeLevel":'$VOLUME'}' -URL='https://pitangui.amazon.com/api/np/command?deviceSerialNumber='$DEVICENUMBER'&deviceType='$DEVICETYPE -curl $URL \ --H "$COOKIE" \ --H "$CSRF" \ ---data "${VOLUMECMD}" \ ---compressed diff --git a/alexa_remote_control.sh b/alexa_remote_control.sh new file mode 100644 index 0000000..a84d4d4 --- /dev/null +++ b/alexa_remote_control.sh @@ -0,0 +1,765 @@ +#!/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-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: v0.7c fixed US version +# 2017-12-19: v0.7d fixed AWK csrf extraction on some systems +# 2017-12-20: v0.7e moved get_devlist after check_status +# 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: v0.8b added echo-spot to ALL group +# 2018-01-28: v0.8c added configurable browser string +# +### +# +# (no BASHisms were used, should run with any shell) +# - requires cURL for web communication +# - (GNU) sed and awk for extraction +# - jq as command line JSON parser (optional for the fancy bits) +# +########################################## + +EMAIL='amazon_account@email.address' +PASSWORD='Very_Secret_Amazon_Account_Password' + +LANGUAGE="de,en" +#LANGUAGE="en-us" + +AMAZON='amazon.de' +#AMAZON='amazon.com' + +ALEXA='layla.amazon.de' +#ALEXA='pitangui.amazon.com' + +# cURL binary +CURL='/usr/bin/curl' + +# 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 +OPTS='--compressed' +#OPTS='-k --compressed' + +# browser identity +BROWSER='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0' + +########################################### +# nothing to configure below here +# +TMP="/tmp" +COOKIE="${TMP}/.alexa.cookie" +DEVLIST="${TMP}/.alexa.devicelist.json" + +GUIVERSION=0 + +LIST="" +LOGOFF="" +COMMAND="" +STATIONID="" +QUEUE="" +SONG="" +TYPE="" +ASIN="" +SEEDID="" +HIST="" +LEMUR="" +CHILD="" +PLIST="" +BLUETOOTH="" + +usage() +{ + echo "$0 [-d |ALL] -e > | -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -r <\"station name\"|stationid> | -s | -t |" + echo " -u | -v | -w | -i | -p | -P | -S | -a | -m [device_1 .. device_X] | -l | -h" + echo " -e : run command" + echo " -b : connect/disconnect/list bluetooth device" + echo " -q : query queue" + echo " -r : play tunein radio" + echo " -s : play library track" + 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" + 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" + 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 + # 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 + ;; + -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" + ;; + -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"}' + ;; + 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 + ;; + "") + ;; + *) + 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 ${COOKIE} +rm -f ${TMP}/.alexa.*.list + +# +# 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 sessiion +# +${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" + +# +# login with filled out form +# !!! referer now contains session in URL +# +${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 "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 > /dev/null + +# +# get CSRF +# +${CURL} ${OPTS} -s -c ${COOKIE} -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\ + -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\ + https://${ALEXA}/api/language > /dev/null + +rm -f "${TMP}/.alexa.header" +rm -f "${TMP}/.alexa.postdata" +rm -f "${TMP}/.alexa.postdata2" +} + +# +# get JSON device list +# +get_devlist() +{ +${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})"\ + "https://${ALEXA}/api/devices-v2/device?cached=false" > ${DEVLIST} +} + +check_status() +{ +# +# bootstrap with GUI-Version writes GUI version to cookie +# returns among other the current authentication state +# + AUTHSTATUS=$(${CURL} ${OPTS} -s -c ${COOKIE} -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L https://${ALEXA}/api/bootstrap?version=${GUIVERSION} | sed -r 's/^.*"authenticated":([^,]+),.*$/\1/g') + + if [ "$AUTHSTATUS" = "true" ] ; then + return 1 + fi + + return 0 +} + +# +# set device specific variables from JSON device list +# +set_var() +{ + DEVICE=$(echo ${DEVICE} | sed -r 's/%20/ /g') + + if [ -z "${DEVICE}" ] ; then + # if no device was supplied, use the first Echo(dot) in device list + echo "setting default device to:" + DEVICE=$(jq -r '[ .devices[] | select(.deviceFamily == "ECHO" or .deviceFamily == "KNIGHT" or .deviceFamily == "ROOK" ) | .accountName] | .[0]' ${DEVLIST}) + 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}) + MEDIAOWNERCUSTOMERID=$(jq --arg device "${DEVICE}" -r '.devices[] | select(.accountName == $device) | .deviceOwnerCustomerId' ${DEVLIST}) + + if [ -z "${DEVICESERIALNUMBER}" ] ; then + echo "ERROR: unkown device dev:${DEVICE}" + exit 1 + fi +} + +# +# list available devices from JSON device list +# +list_devices() +{ + jq -r '.devices[].accountName' ${DEVLIST} +} + +# +# execute command +# +run_cmd() +{ +${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}" +} + +# +# play TuneIn radio station +# +play_radio() +{ +${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\ + "https://${ALEXA}/api/tunein/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&guideId=${STATIONID}&contentType=station&callSign=&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}" +} + +# +# play library track +# +play_song() +{ +${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 "{\"trackId\":\"${SONG}\",\"playQueuePrime\":true}"\ + "https://${ALEXA}/api/cloudplayer/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}&shuffle=false" +} + +# +# play library playlist +# +play_playlist() +{ +${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 "{\"playlistId\":\"${PLIST}\",\"playQueuePrime\":true}"\ + "https://${ALEXA}/api/cloudplayer/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}&shuffle=false" +} + +# +# play PRIME playlist +# +play_prime_playlist() +{ +${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 "{\"asin\":\"${ASIN}\"}"\ + "https://${ALEXA}/api/prime/prime-playlist-queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}" +} + +# +# play PRIME station +# +play_prime_station() +{ +${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 "{\"seed\":\"{\\\"type\\\":\\\"KEY\\\",\\\"seedId\\\":\\\"${SEEDID}\\\"}\",\"stationName\":\"none\",\"seedType\":\"KEY\"}"\ + "https://${ALEXA}/api/gotham/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}" +} + +# +# play PRIME historical queue +# +play_prime_hist_queue() +{ +${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 "{\"deviceType\":\"${DEVICETYPE}\",\"deviceSerialNumber\":\"${DEVICESERIALNUMBER}\",\"mediaOwnerCustomerId\":\"${MEDIAOWNERCUSTOMERID}\",\"queueId\":\"${HIST}\",\"service\":null,\"trackSource\":\"TRACK\"}"\ + "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 + +${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/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} +} + +# +# show Prime stations and playlists +# +show_prime() +{ + FILE=${TMP}/.alexa.${PRIME}.list + + if [ ! -f ${FILE} ] ; then +${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/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 +${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/prime/prime-playlists-by-browse-node?browseNodeId=${I}&deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}" >> ${FILE} + done + fi + fi + jq '.' ${FILE} +} + +# +# 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 + +${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 '.' +} + +# +# deletes a multiroom device +# +delete_multiroom() +{ +${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 DELETE \ + "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%,}]}" + +${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 "${JSON}" \ + "https://${ALEXA}/api/lemur/tail" +} + +# +# list bluetooth devices +# +list_bluetooth() +{ +${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/bluetooth?cached=false" | jq --arg serial "${DEVICESERIALNUMBER}" -r '.bluetoothStates[] | select(.deviceSerialNumber == $serial) | "\(.pairedDeviceList[]?.address) \(.pairedDeviceList[]?.friendlyName)"' +} + +# +# connect bluetooth device +# +connect_bluetooth() +{ +${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 "{\"bluetoothDeviceAddress\":\"${BLUETOOTH}\"}"\ + "https://${ALEXA}/api/bluetooth/pair-sink/${DEVICETYPE}/${DEVICESERIALNUMBER}" +} + +# +# disconnect bluetooth device +# +disconnect_bluetooth() +{ +${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 \ + "https://${ALEXA}/api/bluetooth/disconnect-sink/${DEVICETYPE}/${DEVICESERIALNUMBER}" +} + +# +# logout +# +log_off() +{ +${CURL} ${OPTS} -s -c ${COOKIE} -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\ + https://${ALEXA}/logout > /dev/null + +rm -f ${DEVLIST} +rm -f ${COOKIE} +rm -f ${TMP}/.alexa.*.list +} + +if [ -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 + echo "only logout option present, logging off ..." + log_off + exit 0 +fi + +if [ ! -f ${COOKIE} ] ; then + echo "cookie do not exist. logging in ..." + log_in +fi + +check_status +if [ $? -eq 0 ] ; then + echo "cookie expired, logging in again ..." + log_in + check_status + if [ $? -eq 0 ] ; then + echo "log in failed, aborting" + exit 1 + fi +fi + +if [ ! -f ${DEVLIST} ] ; then + echo "device list do not exist. downloading ..." + get_devlist + if [ ! -f ${DEVLIST} ] ; then + echo "failed to download device list, aborting" + exit 1 + fi +fi + +if [ -n "$COMMAND" -o -n "$QUEUE" ] ; then + if [ "${DEVICE}" = "ALL" ] ; then + 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 + set_var + if [ -n "$COMMAND" ] ; then + echo "sending cmd:${COMMAND} to dev:${DEVICE} type:${DEVICETYPE} serial:${DEVICESERIALNUMBER}" + run_cmd + else + echo "queue info for dev:${DEVICE} type:${DEVICETYPE} serial:${DEVICESERIALNUMBER}" + show_queue + fi + done + else + set_var + if [ -n "$COMMAND" ] ; then + echo "sending cmd:${COMMAND} to dev:${DEVICE} type:${DEVICETYPE} serial:${DEVICESERIALNUMBER}" + 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 + if [ "$BLUETOOTH" = "list" -o "$BLUETOOTH" = "List" -o "$BLUETOOTH" = "LIST" ] ; then + if [ "${DEVICE}" = "ALL" ] ; then + 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 + 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 + echo "disconnecting dev:${DEVICE} type:${DEVICETYPE} serial:${DEVICESERIALNUMBER} from bluetooth" + disconnect_bluetooth + else + set_var + 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 "$LIST" ] ; then + 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 +else + echo "no alexa command received" +fi + +if [ -n "$LOGOFF" ] ; then + echo "logout option present, logging off ..." + log_off +fi diff --git a/alexa_remote_control_plain.sh b/alexa_remote_control_plain.sh new file mode 100644 index 0000000..8132540 --- /dev/null +++ b/alexa_remote_control_plain.sh @@ -0,0 +1,695 @@ +#!/bin/sh +# +# Amazon Alexa Remote Control (PLAIN shell) +# alex(at)loetzimmer.de +# +# 2018-01-10: v0.8c (for updates see http://blog.loetzimmer.de/2017/10/amazon-alexa-hort-auf-die-shell-echo.html) +# +### +# +# (no BASHisms were used, should run with any shell) +# - requires cURL for web communication +# - (GNU) sed and awk for extraction +# +########################################## + +EMAIL='amazon_account@email.address' +PASSWORD='Very_Secret_Amazon_Account_Password' + + +LANGUAGE="de,en" +#LANGUAGE="en-us" + +AMAZON='amazon.de' +#AMAZON='amazon.com' + +ALEXA='layla.amazon.de' +#ALEXA='pitangui.amazon.com' + +# cURL binary +CURL='/usr/bin/curl' + +# 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 +OPTS='--compressed' +#OPTS='-k --compressed' + +# browser identity +BROWSER='Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0' + +########################################### +# nothing to configure below here +# +TMP="/tmp" +COOKIE="${TMP}/.alexa.cookie" +DEVLIST="${TMP}/.alexa.devicelist.json" +DEVTXT="${TMP}/.alexa.devicelist.txt" + +GUIVERSION=0 + +LIST="" +LOGOFF="" +COMMAND="" +STATIONID="" +QUEUE="" +SONG="" +ASIN="" +SEEDID="" +HIST="" +LEMUR="" +CHILD="" +PLIST="" +BLUETOOTH="" + +usage() +{ + echo "$0 [-d ] -e > | -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -r | -s | -t |" + echo " -u | -v | -w | -a | -m [device_1 .. device_X] | -l | -h" + echo " -e : run command" + echo " -q : query queue" + echo " -b : connect/disconnect/list bluetooth device" + echo " -r : play tunein radio" + echo " -s : play library track" + 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" + 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 + ;; + -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" + ;; + -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"}' + ;; + 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 + ;; + "") + ;; + *) + 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 +# +${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 sessiion +# +${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" + +# +# login with filled out form +# !!! referer now contains session in URL +# +${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 "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 > /dev/null + +# +# get CSRF +# +${CURL} ${OPTS} -s -c ${COOKIE} -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\ + -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\ + https://${ALEXA}/api/language > /dev/null + +rm -f "${TMP}/.alexa.header" +rm -f "${TMP}/.alexa.postdata" +rm -f "${TMP}/.alexa.postdata2" +} + +# +# get JSON device list +# +get_devlist() +{ +${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})"\ + "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 +# + AUTHSTATUS=$(${CURL} ${OPTS} -s -c ${COOKIE} -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L https://${ALEXA}/api/bootstrap?version=${GUIVERSION} | sed -r 's/^.*"authenticated":([^,]+),.*$/\1/g') + + 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'` + + ATTR="deviceOwnerCustomerId" + MEDIAID=`grep ${ATTR}\| ${DEVTXT} | sed "s/^.*${ATTR}|//" | sed 's/ /_/g'` + + 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 + if [ "$I" = "ECHO" -o "$I" = "KNIGHT" -o "$I" = "ROOK" ] ; then + 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 + + C=0 + for I in $MEDIAID ; do + if [ $C -eq $IDX ] ; then + MEDIAOWNERCUSTOMERID=$I + break + fi + C=$((C+1)) + done + + 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 +# +run_cmd() +{ +${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}" +} + +# +# play TuneIn radio station +# +play_radio() +{ +${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\ + "https://${ALEXA}/api/tunein/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&guideId=${STATIONID}&contentType=station&callSign=&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}" +} + +# +# play library track +# +play_song() +{ +${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 "{\"trackId\":\"${SONG}\",\"playQueuePrime\":true}"\ + "https://${ALEXA}/api/cloudplayer/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}&shuffle=false" +} + +# +# play library playlist +# +play_playlist() +{ +${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 "{\"playlistId\":\"${PLIST}\",\"playQueuePrime\":true}"\ + "https://${ALEXA}/api/cloudplayer/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}&shuffle=false" +} + +# +# play PRIME playlist +# +play_prime_playlist() +{ +${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 "{\"asin\":\"${ASIN}\"}"\ + "https://${ALEXA}/api/prime/prime-playlist-queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}" +} + +# +# play PRIME station +# +play_prime_station() +{ +${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 "{\"seed\":\"{\\\"type\\\":\\\"KEY\\\",\\\"seedId\\\":\\\"${SEEDID}\\\"}\",\"stationName\":\"none\",\"seedType\":\"KEY\"}"\ + "https://${ALEXA}/api/gotham/queue-and-play?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&mediaOwnerCustomerId=${MEDIAOWNERCUSTOMERID}" +} + +# +# play PRIME historical queue +# +play_prime_hist_queue() +{ +${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 "{\"deviceType\":\"${DEVICETYPE}\",\"deviceSerialNumber\":\"${DEVICESERIALNUMBER}\",\"mediaOwnerCustomerId\":\"${MEDIAOWNERCUSTOMERID}\",\"queueId\":\"${HIST}\",\"service\":null,\"trackSource\":\"TRACK\"}"\ + "https://${ALEXA}/api/media/play-historical-queue" +} + +# +# current queue +# +show_queue() +{ + echo "/api/np/player" +${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}" + echo + echo "/api/np/queue" +${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}" + echo + echo "/api/media/state" + ${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}" + echo +} + +# +# deletes a multiroom device +# +delete_multiroom() +{ +${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 DELETE \ + "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%,}]}" + +${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 "${JSON}" \ + "https://${ALEXA}/api/lemur/tail" +} + +# +# list bluetooth devices +# +list_bluetooth() +{ +${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/bluetooth?cached=false" +} + +# +# connect bluetooth device +# +connect_bluetooth() +{ +${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 "{\"bluetoothDeviceAddress\":\"${BLUETOOTH}\"}"\ + "https://${ALEXA}/api/bluetooth/pair-sink/${DEVICETYPE}/${DEVICESERIALNUMBER}" +} + +# +# disconnect bluetooth device +# +disconnect_bluetooth() +{ +${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 \ + "https://${ALEXA}/api/bluetooth/disconnect-sink/${DEVICETYPE}/${DEVICESERIALNUMBER}" +} + +# +# logout +# +log_off() +{ +${CURL} ${OPTS} -s -c ${COOKIE} -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\ + https://${ALEXA}/logout > /dev/null + +rm -f ${DEVLIST} +rm -f ${DEVTXT} +rm -f ${COOKIE} +} + +if [ -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 + echo "only logout option present, logging off ..." + log_off + exit 0 +fi + +if [ ! -f ${COOKIE} ] ; then + echo "cookie do not exist. logging in ..." + log_in +fi + +check_status +if [ $? -eq 0 ] ; then + echo "cookie expired, logging in again ..." + log_in + check_status + if [ $? -eq 0 ] ; then + echo "log in failed, aborting" + exit 1 + fi +fi + +if [ ! -f ${DEVTXT} ] ; then + echo "device list do not exist. downloading ..." + get_devlist + if [ ! -f ${DEVTXT} ] ; then + echo "failed to download device list, aborting" + exit 1 + fi +fi + +if [ -n "$COMMAND" ] ; then + set_var + if [ -n "$COMMAND" ] ; then + echo "sending cmd:${COMMAND} to dev:${DEVICE} type:${DEVICETYPE} serial:${DEVICESERIALNUMBER}" + 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 + elif [ "$BLUETOOTH" = "list" -o "$BLUETOOTH" = "List" -o "$BLUETOOTH" = "LIST" ] ; then + echo "bluetooth api list:" + list_bluetooth + 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' +else + echo "no alexa command received" +fi + +if [ -n "$LOGOFF" ] ; then + echo "logout option present, logging off ..." + log_off +fi diff --git a/alexia-next.sh b/alexia-next.sh deleted file mode 100644 index 6dfdd0c..0000000 --- a/alexia-next.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -DEVICENUMBER='G090LF1234540B82' -DEVICETYPE='A3S5123456VAYF' -COOKIE='Cookie: x-amzn-dat-gui-client-v=1.24.986.0; aws-target-static-id=14712345678-839972; aws-session-id=168-0412369-4130549; aws-session-id-time=2106194071l; aws-x-main=PgpqvER9Wibauhf4zfSDriNBlBILfMnk; aws-at-main=Atza|IwEBIAFsntUWFc_PZvFde5drDzPNXXXXXXXXXHPqI53aQNl-Fu83eAMUsFJmMtamv_4mLKEh6N82lqq8OHA8SKgh44X2MZwTguXNcUvd0-RVQyayT2lQ86JjoXvJrlMlrZIs7kJLWBryCGJEXXXXXXXXXXFJQpHhnGeC72ljNT_nVObL_wZoYhMFHVMXGiYjFyrb-QcjYY-I3ar-Q-kLAmzZYQIKU-mZ8GChNefKuG_x4ZZ2A06XXXXXXXXXOhX9w94vgmn2y3Qr9Q0rEi8K--UoFuTQ54GArubNsofNLh4U2dov8D7Gs3GoW6Hp2zEwt7cyumpRR_vceAz3aUjskoNKXsgDfpPZrebl-0R4bC6BfIasyoDOX9XXXXXXXXjRihKSxf4gs79p1ExYBmaBOX6glBrsKRqOXfk6Vh87_96uQ; aws-userInfo=%7B%22arn%22%3A%22arn%3Aaws%3Aiam%3A%3A314911114493%3Aroot%22%2C%22alias%22%3A%22%22%2C%22username%22%3A%22MeinUser%2520Name%22%2C%22keybase%22%3A%22dhVv%2Fmb%2BJyCH%2BJA6zwfighlgs5jc0W0mVYXkLBkb77M%5Cu003d%22%2C%22issuer%22%3A%22https%3A%2F%2Fwww.amazon.com%2Fap%2Fsignin%22%7D; __utmv=191231197.PgpqvER9Wibauhf4zfSDXXXXXXXXk; s_dslv=1475471239073; regStatus=registered; aws-target-visitor-id=1475445653051-715340.26_4; aws-target-data=%7B%22support%22%3A%221%22%7D; s_pers=%20s_ev15%3D%255B%255B%2527Typed%252FBookmarked%2527%252C%25212375474893114%2527%255D%255D%7C1632141293114%3B%20s_fid%3D0523A63XXXXXXXXX3C9679E640DA3570%7C1538547024210%3B%20s_dl%3D1%7C1475481765932%3B%20gpv_page%3Dno%2520value%7C1475481765939%3B; session-id=151-3512347-8293901; session-id-time=2106212731l; s_fid=71689F1234D6D238-299FD69294EFC3C1; s_nr=1475492764173-Repeat; aws-ubid-main=151-3388123-1263140; aws-session-token="lprp9YroXXXXXXXXXz5EFfSaPt96R0oSBWL2dR/AGFdlmk4QwRcVPXXXXXXXrmZ5kfHBdNwvSWWMKTnYU0pB5CE2XXXXXXUk6SIuaQYti17ZQ8B3NGwyCKNs7Lil+kz9qonZS5/cwtmrWHCe7SGx/QmPXXXXXXX89WbiV2xwTRkhVw6hTjO+8WUOXXXXXXXXi8MaR196v+Ewflx7dd3ouTVtY+hbJE4XSs="; __utma=194891197.1975555581.1444474071.1475495555.1475555580.5; __utmz=194555597.1475555680.5.2.utmccn=(referral)|utmcsr=console.aws.amazon.com|utmcct=/cloudwatch/home|utmcmd=referral; ubid-main=152-8362434-2710144; session-token="Y2Eoqs81+wIbanI7mPonK9t7gFvH7ZdTG2OxCZB3orqTILNY5ev4cFBWiSPTl2u8nFNXNmwtFP6RxnPBTunkdbHfpos8WYYOX7gEF52wnn5J3SknsvO99+MAT5GeyMR4JRa+ZOX+DcnIt8xtgYY2sSaEZ3JspjrQF+M9PUkuT9D4aYbZGRNa1BkFvoOt8Kettr9xxS/ODPfXEaE6xJr7qyPv0IQ3K6e+FbfGk3AWypY="; x-main=4r7ZrJJV4BT1R2sWn2W4NpiXZm9RSJLD; at-main=Atza|IwEEEEE4EikDns-CTHnGRqXXXcnnaij75AfcF1-XOBvphYTp_ATJHu6f0vDgMI7B1ApWn51cP6bqvG-XXXXeTPD8t7jl5El_FdhlYn3mmtn476FfnI5E7anoZNSKLyE-jauNJssBeGUjsSd0z8746mujYcJbKQsMMVVUlxcu4jXXxXXYcC4GasQyvOIp92wY-TPKAOys8NV0-yXH4xTGBzgJ0zzJa5JvyRz2v0x3fijvyXXXXXXSDhZtaDOZ6GOQlYrsUmudsorYUcUT52x0EYavXXXXX92cmgkMUzuTxppV1SpLk2Wk63LQQkCCZLs6_HWlnX2Saf0zgc13BdUehhSBiYHzJJeg1Ys7g68GLG5IuSGvyGe8ebmcN9KUDdD2CwphaeW34iy6dVKGbUpJ3GKVlz; sess-at-main="eSHstcxm5Krki5T1EAcQyk9XXXXXXqMbqcJqDBaA="; csrf=-2012345694' -CSRF='csrf: -2081234594' - -CMD='{"type":"NextCommand"}' -URL='https://pitangui.amazon.com/api/np/command?deviceSerialNumber='$DEVICENUMBER'&deviceType='$DEVICETYPE -curl $URL \ --H "$COOKIE" \ --H "$CSRF" \ ---data "${CMD}" \ ---compressed diff --git a/alexia-play.sh b/alexia-play.sh deleted file mode 100644 index 850ca53..0000000 --- a/alexia-play.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -DEVICENUMBER='G090LF1234540B82' -DEVICETYPE='A3S5123456VAYF' -COOKIE='Cookie: x-amzn-dat-gui-client-v=1.24.986.0; aws-target-static-id=14712345678-839972; aws-session-id=168-0412369-4130549; aws-session-id-time=2106194071l; aws-x-main=PgpqvER9Wibauhf4zfSDriNBlBILfMnk; aws-at-main=Atza|IwEBIAFsntUWFc_PZvFde5drDzPNXXXXXXXXXHPqI53aQNl-Fu83eAMUsFJmMtamv_4mLKEh6N82lqq8OHA8SKgh44X2MZwTguXNcUvd0-RVQyayT2lQ86JjoXvJrlMlrZIs7kJLWBryCGJEXXXXXXXXXXFJQpHhnGeC72ljNT_nVObL_wZoYhMFHVMXGiYjFyrb-QcjYY-I3ar-Q-kLAmzZYQIKU-mZ8GChNefKuG_x4ZZ2A06XXXXXXXXXOhX9w94vgmn2y3Qr9Q0rEi8K--UoFuTQ54GArubNsofNLh4U2dov8D7Gs3GoW6Hp2zEwt7cyumpRR_vceAz3aUjskoNKXsgDfpPZrebl-0R4bC6BfIasyoDOX9XXXXXXXXjRihKSxf4gs79p1ExYBmaBOX6glBrsKRqOXfk6Vh87_96uQ; aws-userInfo=%7B%22arn%22%3A%22arn%3Aaws%3Aiam%3A%3A314911114493%3Aroot%22%2C%22alias%22%3A%22%22%2C%22username%22%3A%22MeinUser%2520Name%22%2C%22keybase%22%3A%22dhVv%2Fmb%2BJyCH%2BJA6zwfighlgs5jc0W0mVYXkLBkb77M%5Cu003d%22%2C%22issuer%22%3A%22https%3A%2F%2Fwww.amazon.com%2Fap%2Fsignin%22%7D; __utmv=191231197.PgpqvER9Wibauhf4zfSDXXXXXXXXk; s_dslv=1475471239073; regStatus=registered; aws-target-visitor-id=1475445653051-715340.26_4; aws-target-data=%7B%22support%22%3A%221%22%7D; s_pers=%20s_ev15%3D%255B%255B%2527Typed%252FBookmarked%2527%252C%25212375474893114%2527%255D%255D%7C1632141293114%3B%20s_fid%3D0523A63XXXXXXXXX3C9679E640DA3570%7C1538547024210%3B%20s_dl%3D1%7C1475481765932%3B%20gpv_page%3Dno%2520value%7C1475481765939%3B; session-id=151-3512347-8293901; session-id-time=2106212731l; s_fid=71689F1234D6D238-299FD69294EFC3C1; s_nr=1475492764173-Repeat; aws-ubid-main=151-3388123-1263140; aws-session-token="lprp9YroXXXXXXXXXz5EFfSaPt96R0oSBWL2dR/AGFdlmk4QwRcVPXXXXXXXrmZ5kfHBdNwvSWWMKTnYU0pB5CE2XXXXXXUk6SIuaQYti17ZQ8B3NGwyCKNs7Lil+kz9qonZS5/cwtmrWHCe7SGx/QmPXXXXXXX89WbiV2xwTRkhVw6hTjO+8WUOXXXXXXXXi8MaR196v+Ewflx7dd3ouTVtY+hbJE4XSs="; __utma=194891197.1975555581.1444474071.1475495555.1475555580.5; __utmz=194555597.1475555680.5.2.utmccn=(referral)|utmcsr=console.aws.amazon.com|utmcct=/cloudwatch/home|utmcmd=referral; ubid-main=152-8362434-2710144; session-token="Y2Eoqs81+wIbanI7mPonK9t7gFvH7ZdTG2OxCZB3orqTILNY5ev4cFBWiSPTl2u8nFNXNmwtFP6RxnPBTunkdbHfpos8WYYOX7gEF52wnn5J3SknsvO99+MAT5GeyMR4JRa+ZOX+DcnIt8xtgYY2sSaEZ3JspjrQF+M9PUkuT9D4aYbZGRNa1BkFvoOt8Kettr9xxS/ODPfXEaE6xJr7qyPv0IQ3K6e+FbfGk3AWypY="; x-main=4r7ZrJJV4BT1R2sWn2W4NpiXZm9RSJLD; at-main=Atza|IwEEEEE4EikDns-CTHnGRqXXXcnnaij75AfcF1-XOBvphYTp_ATJHu6f0vDgMI7B1ApWn51cP6bqvG-XXXXeTPD8t7jl5El_FdhlYn3mmtn476FfnI5E7anoZNSKLyE-jauNJssBeGUjsSd0z8746mujYcJbKQsMMVVUlxcu4jXXxXXYcC4GasQyvOIp92wY-TPKAOys8NV0-yXH4xTGBzgJ0zzJa5JvyRz2v0x3fijvyXXXXXXSDhZtaDOZ6GOQlYrsUmudsorYUcUT52x0EYavXXXXX92cmgkMUzuTxppV1SpLk2Wk63LQQkCCZLs6_HWlnX2Saf0zgc13BdUehhSBiYHzJJeg1Ys7g68GLG5IuSGvyGe8ebmcN9KUDdD2CwphaeW34iy6dVKGbUpJ3GKVlz; sess-at-main="eSHstcxm5Krki5T1EAcQyk9XXXXXXqMbqcJqDBaA="; csrf=-2012345694' -CSRF='csrf: -2081234594' - -CMD='{"type":"PlayCommand"}' -URL='https://pitangui.amazon.com/api/np/command?deviceSerialNumber='$DEVICENUMBER'&deviceType='$DEVICETYPE -curl $URL \ --H "$COOKIE" \ --H "$CSRF" \ ---data "${CMD}" \ ---compressed