Dienstag, 10. Oktober 2017

Amazon Alexa per Shell fernsteuern (Echo remote control)

Nachdem Amazon mit dem Multiroom Feature der Echos meine alten Multiroom Audio Bemühungen quasi nebenbei erledigt hat, fehlte mir zu meinem Glück noch die Fernsteuerung meiner Echos.

Wenn ich nach Hause komme, soll die Home Automation bitte auch gleich das Radio auf den Amazon Echos einschalten.
Für den umgekehrten Weg nutze ich HA-Bridge, welches meine Intertechno Billigsteckdosen per Alexa Sprachkommando schaltet. Daher kenne ich auch die Möglichkeit, über einen Browser auf das Alexa Interface zuzugreifen.

Was ein Browser kann, das sollte sich doch auch per Shell bewältigen lassen...

Ich habe die Entwicklertools im Firefox benutzt (Shift + Strg + i) und auf dem Reiter "Netzwerkanalyse" den Datenverkehr verfolgt. Zusätzlich habe ich Javascript abgeschaltet, da ich nicht mit irgendwelchen riesigen Frameworks hantieren wollte - und meine Shell kann kein Javascript.

Zunächst hatte mein cURL keine Cookies speichern wollen. Das änderte sich erst, nachdem ich den "User-Agent" Header hinzugefügt habe. 

Komischerweise schlägt auf diese Weise selbst im Browser die erste Anmeldung fehl. Bei genauerer Betrachtung ändert sich allerdings die Referer URL, in die nun die Session kodiert ist. So klappt dann auch das Login.

Außerdem ist zwingend ein "Accept-Language:" Header erforderlich - daraus leitet Amazon wahrscheinlich die Region für das Login ab.
Update 2018-01-28: Nachdem ich einfach nur "Mozilla/5.0" als Browser String verwendet hatte, funktionierte es auch eine Weile. Inzwischen muss ich einen real existierenden (aktuellen) Browser String verwenden!

Im Alexa Webfrontend angekommen, sind hauptsächlich die POST Anfragen interessant, immer dann habe ich eine Aktion im Frontend ausgelöst.

Da bei vielen Anfragen nur JSON Antworten zurückkommen, brauchte ich noch einen JSON Parser für die Kommandozeile und bin auf jq gestoßen. Die Einarbeitung ist etwas kryptisch, aber die Ergebnisse sehr lohnenswert.
Und wenn man schon einen JSON Parser hat, kann man eigentlich auch gleich die TuneIn Sender per Sendernamen suchen lassen und aus dem ersten Antworteintrag die StationID extrahieren...

Es gibt auch eine Version ohne JSON Parser mit weniger Funktionen (kein -i, -p, -P, -S, keine Radio Stationsnamen, kein ALL Device).

Funktionsbeschreibung

Am Anfang ist es evtl hilfreich eine Liste der Alexa kompatiblen Geräte zu holen
alexa_remote_control.sh -a
Wird später kein Gerät ausgewählt, nutzt das Script immer das erste Echo(dot)-Gerät in der Liste.
Ein Gerät darf selbstverständlich auch eine Multiroom-Gruppe sein.

Jetzt kann man bei TuneIn einen Sender suchen lassen und diesen auf einem Gerät (z.B. Wohnzimmer) in der Liste abspielen...
alexa_remote_control.sh -d Wohnzimmer -r "radioeins vom rbb"
...oder man kennt die TuneIn StationID.
alexa_remote_control.sh -d Wohnzimmer -r s25111
Möchte man sämtliche Wiedergaben stoppen, gibt es ein spezielles Gerät ALL.
alexa_remote_control.sh -d ALL -e pause

Es gibt weiterhin ein paar Funktionen, um die Amazon Bibliotheken abzufragen:
alexa_remote_control.sh -i
Legt eine Liste der zu Amazon hochgeladenen Tracks an "/tmp/.alexa.IMPORTED.list". Die enthaltene trackId wird für die für Option -s verwendet.
alexa_remote_control.sh -p
Legt eine Liste der bei Amazon gekauften Tracks an "/tmp/.alexa.PURCHASES.list". Die enthaltene trackId wird für die für Option -s verwendet.
alexa_remote_control.sh -P
Legt eine Liste aller Amazon Playlists an "/tmp/.alexa.prime-playlist-browse-nodes.list". Die enthaltene asin wird für die für Option -t verwendet
alexa_remote_control.sh -S
Legt eine Liste aller Amazon Stationen an "/tmp/.alexa.prime-sections.list". Die enthaltene seedId wird für die für Option -u verwendet

Um einen Bibliothekstrack abzuspielen, benötigt man die trackId
alexa_remote_control.sh -d Esszimmer -s 162ab3024d22a-401a-93f4-123456-7890ab
Um eine Prime Playlist abzuspielen, benötigt man die asin
alexa_remote_control.sh -d Esszimmer -t B01DWABCDE
Um einen Prime Radiostation abzuspielen, benötigt man die seedId
alexa_remote_control.sh -d Esszimmer -u A30GBP8ABCDEF
Als Prime Benutzer kann man per Sprachkommando auch bestimmte Interpreten spielen lassen. In der Oberfläche fehlt eine entsprechende Steuermöglichkeit. Fragt man den aktuellen Play-Status in diesem Fall aber mit de -q Option ab, so erhält man eine queueId (Device spezifisch), die man später mit folgendem Kommando erneut abspielen kann:
alexa_remote_control.sh -d Esszimmer -v 1f4919d7-45db-45cf-a47f-123456-7890ab

Es können nun auch Multiroom Gruppen angelegt und gelöscht werden
alexa_remote_control.sh -m <Gruppe>
löscht die Multiroomgruppe
alexa_remote_control.sh -m <Gruppe> <Gerät1> ... <GerätX>
löscht die Multiroomgruppe und legt sie mit den Geräten 1..X neu an

Bluetooth Verbindungen werden mit der Option -b hergestellt/getrennt/gelistet:
alexa_remote_control.sh -d Esszimmer -b list
Listet alle bekannten Bluetooth Geräte für Esszimmer. Als Adresse kann je nach Bluetooth-Gerät evtl. auch ein mehrstelliger Code angezeigt werden - es muss nicht zwangsläufug eine Bluetooth HW-Adresse sein.
alexa_remote_control.sh -d Esszimmer -b "AA:BB:CC:DD:EE:FF"
Verbindet Gerät "AA:BB:CC:DD:EE:FF"
alexa_remote_control.sh -d Esszimmer -b
Trennt Bluetooth im Esszimmer
Evtl. muss das erste Pairing zuvor einmalig mit der Alexa App erfolgt sein.

Für die aktuellen Abspielinformationen (Option -q) kommen prinzipiell drei URLs zum Einsatz (ab Zeile 535):
https://layla.amazon.de/api/np/player?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}&lemurId=${PARENTID}&lemurDeviceType=${PARENTDEVICE}
https://layla.amazon.de/api/media/state?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}
https://layla.amazon.de/api/np/queue?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}
Die Ausgaben werden momentan von "jq" ungefiltert ausgegeben.

Noch ein Wort zum Cookie; das Script (also eigentlich cURL) legt eine Datei /tmp/.alexa.cookie an, in die aktuell gültigen Cookie Infrmationen abgelegt sind. Analog dazu existiert eine Session auf den Amazon Servern. Bei jedem Script-Start wird überprüft, ob die Session noch gültig ist. Falls nicht, beginnt der Login Prozess erneut und die "frischen" Session Informationen werden ins Cookie geschrieben.

Zum Einen erzeugt man auf diese Weise nicht unnötig viele Login/Logout Vorgänge, die Amazon evtl. zur Sperrung veranlassen könnte. Allerdings bedeutet das auch, dass die aktuelle Session auch bei Amazon aktiv ist. D.h. wenn man das Script auf hunderten verschiedener Geräte ausführt, sollte man vielleicht lieber mit dem "-l" Parameter die Session beenden (damit wird die Cookie-Datei ebenfalls gelöscht).

Auf einem Multi-User System ist das Cookie außerdem für jeden lesbar (kann so aber auch von mehreren Prozessen verwendet werden). Schreiben darf allerdings nur der Benutzer, dem das Cookie gehört - wenn man als "root" getestet hat, kann eine "normaler Benutzer" das Cookie nie erneuern!!!

Fertiges Script

(download link / Version ohne "jq")
#
# 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
# 2018-02-17: v0.8d no need to write the cookie file on every "check_status"
#
###
#
# (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)
#
##########################################

(see download links above)