Better Alexa Multiroom Volume Control & Alarm Scraping

Hi, i was switching my system from Google Cast Multiroom over to Alexa for various reasons. However, one thing that consistently annoyed me, was the lack of fine tuned volume control for Alexa in different rooms.

Logging in to the website to select each Echo and choose the volume was super annoying. On top, the spotify volume control turned all the echos to the same volume level. This is super impractical since the echo 2nd gen much louder than the rest.

So, i set out to build my own (google home) inspired volume control for Alexa.
It all works thanks to the genious Alexander Noack and his Shell Script for logging into your Alexa account and getting the data. More infos: http://blog.loetzimmer.de/2017/10/amazon-alexa-hort-auf-die-shell-echo.html

In order to make this work with shell_command i had to use the plain version (without JQ Parser) as this is not supported by the shell_command. Furthermore, shell_command does not support multiple commands ala:
"do this" | "then that" | "then that"

In order to get it working i had to write extra shell scripts and simply run 1 dynamic command from a home assistant automation that triggers the shell script.

This is how it currently looks:

alexa

I created 5 input_number sliders. (1 for each echo playing in the group, and 1 for the group)
Scale 1 - 100 for the volume levels you can set for the echos.

If you turn up the volume level of the original echos, the group volume level is turned up by a 1/4 of it respectively Meaning:

Volume 1: 0%
Volume 2: 0%
Voume 3: 0%
Volume 4: 100%

Results in 25% Volumelevel for the Group.

Reflecting the groupvolume level to the individual echos was a much harder task and i used some formulas to calculate the required percentage change.
If you turn down the groupvolume, the biggest indiviual volume levels will turn down fast than the lower volumelevels until it levels out at 0. However, keeping the approximated overall distribution of the volume levels
Vice Verca for the turning up the groupvolume. The lowest individual volume levels will rise faster until every volume meets again at 100%.

On top, i cut the volume level i can set for my echo 2nd gen (remember, i said its much louder than the rest) by 50%. So 50% overall volume level will be the maximum the echo will reach.

In order to also get individual volumelevels you manually set (voice or direct input) reflected into the home assistant I have an extra script that scrapes the volume levels periodicly and posts them via the home assistant API. (I only run this script if something is playing, and I am at home)

Please note: I’m not at all a programmer… This took me way too much time and nervers and my code most probably is ugly and can be optimized heavily. Please do so and share how to do this :slight_smile: Would love to learn from you! - Maybe somebody could use parts of this to make a component for Alexa speakers, that would be a dream… anyway here is what you need to make it happen:

automation:

- alias: setslidergroup
  condition:
    - condition: state
      entity_id: 'input_boolean.gruppe2'
      state: 'off'
  trigger:
    - platform: state
      entity_id: input_number.alexa_wohnzimmer
    - platform: state
      entity_id: input_number.alexa_schlafzimmer
    - platform: state
      entity_id: input_number.alexa_kuche
    - platform: state
      entity_id: input_number.alexa_bad
  action:
  - service: input_boolean.turn_on
    data:
      entity_id: input_boolean.gruppe
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_gruppe
      value: >
            {% if ((states.input_number.alexa_gruppe.state | int) + (((trigger.to_state.state | int - trigger.from_state.state | int) / 4) | round)) < 0  %}
               {% set value = (0) %}
            {% else %}
              {% set value = states.input_number.alexa_gruppe.state | int + ((trigger.to_state.state | int - trigger.from_state.state | int) / 4) | round %}
            {% endif %}
            {{value}}
  - service: input_boolean.turn_off
    data:
      entity_id: input_boolean.gruppe
     
- alias: setsliderindividual
  condition:
    - condition: state
      entity_id: 'input_boolean.gruppe'
      state: 'off'
  trigger:
    - platform: state
      entity_id: input_number.alexa_gruppe
  action:
  - service: input_boolean.turn_on
    data:
      entity_id: input_boolean.gruppe2  
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_wohnzimmer
      value: >
            {% if (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) <= 0  %}
               {% set value = ((states.input_number.alexa_wohnzimmer.state | int) * ((trigger.to_state.state | int / trigger.from_state.state | int)) ) %}
            {%-elif (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) > 0 %}
               {% set value = (((trigger.to_state.state | int - trigger.from_state.state | int) / ( 100 - trigger.from_state.state | int) * (( 100 - states.input_number.alexa_wohnzimmer.state | int))) + (states.input_number.alexa_wohnzimmer.state | int)) %}
            {% endif %}
            {{value}}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_bad
      value: >
            {% if (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) <= 0  %}
               {% set value = ((states.input_number.alexa_bad.state | int) * ((trigger.to_state.state | int / trigger.from_state.state | int)) ) %}
            {%-elif (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) > 0 %}
               {% set value = (((trigger.to_state.state | int - trigger.from_state.state | int) / ( 100 - trigger.from_state.state | int) * (( 100 - states.input_number.alexa_bad.state | int))) + (states.input_number.alexa_bad.state | int)) %}
            {% endif %}
            {{value}}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_schlafzimmer
      value: >
            {% if (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) <= 0  %}
               {% set value = ((states.input_number.alexa_schlafzimmer.state | int) * ((trigger.to_state.state | int / trigger.from_state.state | int)) ) %}
            {%-elif (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) > 0 %}
               {% set value = (((trigger.to_state.state | int - trigger.from_state.state | int) / ( 100 - trigger.from_state.state | int) * (( 100 - states.input_number.alexa_schlafzimmer.state | int))) + (states.input_number.alexa_schlafzimmer.state | int)) %}
            {% endif %}
            {{value}}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_kuche
      value: >
            {% if (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) <= 0  %}
               {% set value = ((states.input_number.alexa_kuche.state | int) * ((trigger.to_state.state | int / trigger.from_state.state | int)) ) %}
            {%-elif (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) > 0 %}
               {% set value = (((trigger.to_state.state | int - trigger.from_state.state | int) / ( 100 - trigger.from_state.state | int) * (( 100 - states.input_number.alexa_kuche.state | int))) + (states.input_number.alexa_kuche.state | int)) %}
            {% endif %}
            {{value}}
  - service: input_boolean.turn_off
    data:
      entity_id: input_boolean.gruppe2
      
#########################################################################################################################################################

- alias: VolumecontrolWohnzimmer
  trigger:
    - platform: state
      entity_id: input_number.alexa_wohnzimmer
  action:
  - service: shell_command.volume
    data_template:
      level: -d Wohnzimmer -e vol:{{states.input_number.alexa_wohnzimmer.state | int}}

- alias: VolumecontrolSchlafzimmer
  trigger:
    - platform: state
      entity_id: input_number.alexa_schlafzimmer
  action:
  - service: shell_command.volume
    data_template:
      level: -d Schlafzimmer -e vol:{{states.input_number.alexa_schlafzimmer.state | int}}
      
- alias: VolumecontrolKuche
  trigger:
    - platform: state
      entity_id: input_number.alexa_kuche
  action:
  - service: shell_command.volume
    data_template:
      level: -d Küche -e vol:{{(states.input_number.alexa_kuche.state | int / 2) | int }}    

- alias: VolumecontrolBad
  trigger:
    - platform: state
      entity_id: input_number.alexa_bad
  action:
  - service: shell_command.volume
    data_template:
      level: -d Bad -e vol:{{states.input_number.alexa_bad.state | int}}   
      
#################################################  VOLUME CONTROL UPDATE ###################

- alias: UpdateVolumeSystemstart
  trigger:
    - platform: homeassistant
      event: start
  action:
  - service: shell_command.volumeupdate
###############################################  periodicly update if home, tag/nacht & spotify is playing 
- alias: UpdateVolumeperiodicly
  trigger:
    - platform: time
      minutes: '/10'
      seconds: 0
  condition:
    condition: and
    conditions:
      - condition: template
        value_template: "{% if is_state('media_player.spotify', 'playing') %}true{% endif %}"
      - condition: state
        entity_id: device_tracker.marcus_marcus
        state: 'home'
      - condition: template
        value_template: "{% if not is_state('input_select.mode', 'Away') %}true{% endif %}"      
  action:
  - service: shell_command.volumeupdate

bash:

#!/bin/sh
# This is a comment!
volumewohnzimmer=$(/share/alexa_remote_control_plain_volume.sh -q -d Wohnzimmer | grep -E '"volume":([0-9])*' -o | grep -E -o '([0-9])*')
volumeschlafzimmer=$(/share/alexa_remote_control_plain_volume.sh -q -d Schlafzimmer | grep -E '"volume":([0-9])*' -o | grep -E -o '([0-9])*')
volumebad=$(/share/alexa_remote_control_plain_volume.sh -q -d Bad | grep -E '"volume":([0-9])*' -o | grep -E -o '([0-9])*')
volumekuche=$(/share/alexa_remote_control_plain_volume.sh -q -d Küche | grep -E '"volume":([0-9])*' -o | grep -E -o '([0-9])*')
answer="$((($volumekuche +1) * 2 -2))"
#echo $answer

curl -X POST -H "x-ha-access: <YOURPASSWORD>" -H "Content-Type: application/json"  -d '{"entity_id": "input_number.alexa_wohnzimmer", "value":"'"$volumewohnzimmer"'"}' http:///HOMEASSISTANT_IP:8123/api/services/input_number/set_value
curl -X POST -H "x-ha-access: <YOURPASSWORD>" -H "Content-Type: application/json"  -d '{"entity_id": "input_number.alexa_schlafzimmer", "value":"'"$volumeschlafzimmer"'"}' http:///HOMEASSISTANT_IP:8123/api/services/input_number/set_value
curl -X POST -H "x-ha-access: <YOURPASSWORD>" -H "Content-Type: application/json"  -d '{"entity_id": "input_number.alexa_kuche", "value":"'"$answer"'"}' http://HOMEASSISTANT_IP:8123/api/services/input_number/set_value
curl -X POST -H "x-ha-access: <YOURPASSWORD>" -H "Content-Type: application/json"  -d '{"entity_id": "input_number.alexa_bad", "value":"'"$volumebad"'"}' http://HOMEASSISTANT_IP:8123/api/services/input_number/set_value

shell_command:

volume: /share/alexa_remote_control_plain_volume.sh {{level}}
volumeupdate: /share/alexavolume_plain.sh

2 Likes

alexa_remote_control_plain_volume.sh:
(Based on: https://loetzimmer.de/patches/alexa_remote_control_plain.sh with only a tiny change)
(If not possible to post here, please let me know and i will remove it)

#!/bin/sh
#
# Amazon Alexa Remote Control (PLAIN shell)
#  alex(at)loetzimmer.de
#
# 2018-03-09: v0.9a (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='YOUR ALEXA ACCOUNT EMAIL'
PASSWORD='YOUR ALEXA ACCOUNT PWD'

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
#  --http1.1 : cURL defaults to HTTP/2 on HTTPS connections if available
OPTS='--compressed --http1.1'
#OPTS='-k --compressed --http1.1'

# 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=""
LASTALEXA=""

usage()
{
	echo "$0 [-d <device>] -e <pause|play|next|prev|fwd|rwd|shuffle|vol:<0-100>> | -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -r <stationid> | -s <trackID> | -t <ASIN> |"
	echo "         -u <seedID> | -v <queueID> | -w <playlistId> | -a | -m <multiroom_device> [device_1 .. device_X] | -lastalexa | -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 "   -lastalexa : print serial number that received the last voice command"
	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"
			;;
		-lastalexa)
			LASTALEXA="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 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"

#
# 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)"

	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
	
#
# 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.login"
rm -f "${TMP}/.alexa.header"
rm -f "${TMP}/.alexa.header2"
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 -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/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}"
}

#
# device that sent the last command
# (by Markus Wennesheimer)
#
last_alexa()
{
${CURL} ${OPTS} -s -b ${COOKIE} -A "Mozilla/5.0" -H "DNT: 1" -H "Connection: keep-alive" -L\
 -H "Content-Type: application/json; charset=UTF-8" -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\
 -H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X GET \
 "https://${ALEXA}/api/activities?startTime=&size=1&offset=1" | sed -r 's/^.*serialNumber":"([^"]+)".*$/\1/'
 }

#
# 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 "$LASTALEXA" -a -z "$BLUETOOTH" -a -z "$LEMUR" -a -z "$PLIST" -a -z "$HIST" -a -z "$SEEDID" -a -z "$ASIN" -a -z "$QUEUE" -a -z "$COMMAND" -a -z "$STATIONID" -a -z "$SONG" -a -n "$LOGOFF" ] ; then
	echo "only logout option present, logging off ..."
	log_off
	exit 0
fi

if [ ! -f ${COOKIE} ] ; then
	echo "cookie does 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 does 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'
elif [ -n "$LASTALEXA" ] ; then
	last_alexa
else
	echo "no alexa command received"
fi

if [ -n "$LOGOFF" ] ; then
	echo "logout option present, logging off ..."
	log_off
fi
1 Like

On top, i wanted to be able to scrape the alarms set via voice into my home assistant instance to use it for further automation. It also uses the script above as a basis, but i added the notification API URL and was able to scrape the alarm from Alexa and if it is turned on or off. I let this run periodicly every 2h to check for a new alarm.

However, there are still some problems with the implementation:

  1. If you have more than 1 alarm on your echo it will only show 1 of them
  2. If you create an alarm on alexa, a new one seems to be created all the time instead of modifying the first one (hence problem 1.)
  3. I did not yet find a way to toggle the alarm from homeassistant within Alexa. However, it should be possible since its available via the webinterface.

automation:

##################################################### update alarm  ######################  
- alias: Updatealarmperiodicly
  trigger:
    - platform: time
      minutes: '/120'
      seconds: 0
  condition:
    condition: and
    conditions:
      - condition: state
        entity_id: device_tracker.marcus_marcus
        state: 'home'
      - condition: template
        value_template: "{% if not is_state('input_select.mode', 'Away') %}true{% endif %}"      
  action:
  - service: shell_command.alarm

bash:

#!/bin/sh
# This is a comment!
a=$(/share/alexa_remote_control_plain_alarm.sh -q -d Schlafzimmer | grep "originalTime" | grep -E '([0-9])([0-9]):([0-9])([0-9])' -o -m 1)
b=$(/share/alexa_remote_control_plain_alarm.sh -q -d Schlafzimmer | grep "ON" -o)
curl -X POST -H "x-ha-access: <YOURPASSWORD>" -H "Content-Type: application/json"  -d '{"state": "'"$a"'", "attributes": {"unit_of_measurement":"'"$b"'"}}' http://HOMEASSISTANT_IP:8123/api/states/sensor.alexaalarm

alexa_remote_control_plain_alarm.sh.sh:
(Based on: https://loetzimmer.de/patches/alexa_remote_control_plain.sh with only a tiny change)
(If not possible to post here, please let me know and i will remove it)

#!/bin/sh
#
# Amazon Alexa Remote Control (PLAIN shell)
#  alex(at)loetzimmer.de
#
# 2018-03-09: v0.9a (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='ALEXA USERNAME'
PASSWORD='YOUR ALEXA PWD'

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
#  --http1.1 : cURL defaults to HTTP/2 on HTTPS connections if available
OPTS='--compressed --http1.1'
#OPTS='-k --compressed --http1.1'

# 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=""
LASTALEXA=""

usage()
{
	echo "$0 [-d <device>] -e <pause|play|next|prev|fwd|rwd|shuffle|vol:<0-100>> | -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -r <stationid> | -s <trackID> | -t <ASIN> |"
	echo "         -u <seedID> | -v <queueID> | -w <playlistId> | -a | -m <multiroom_device> [device_1 .. device_X] | -lastalexa | -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 "   -lastalexa : print serial number that received the last voice command"
	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"
			;;
		-lastalexa)
			LASTALEXA="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 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"

#
# 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)"

	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
	
#
# 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.login"
rm -f "${TMP}/.alexa.header"
rm -f "${TMP}/.alexa.header2"
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 -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/notifications"
 ${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/notifications?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}"
}

#
# device that sent the last command
# (by Markus Wennesheimer)
#
last_alexa()
{
${CURL} ${OPTS} -s -b ${COOKIE} -A "Mozilla/5.0" -H "DNT: 1" -H "Connection: keep-alive" -L\
 -H "Content-Type: application/json; charset=UTF-8" -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\
 -H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X GET \
 "https://${ALEXA}/api/activities?startTime=&size=1&offset=1" | sed -r 's/^.*serialNumber":"([^"]+)".*$/\1/'
 }

#
# 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 "$LASTALEXA" -a -z "$BLUETOOTH" -a -z "$LEMUR" -a -z "$PLIST" -a -z "$HIST" -a -z "$SEEDID" -a -z "$ASIN" -a -z "$QUEUE" -a -z "$COMMAND" -a -z "$STATIONID" -a -z "$SONG" -a -n "$LOGOFF" ] ; then
	echo "only logout option present, logging off ..."
	log_off
	exit 0
fi

if [ ! -f ${COOKIE} ] ; then
	echo "cookie does 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 does 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'
elif [ -n "$LASTALEXA" ] ; then
	last_alexa
else
	echo "no alexa command received"
fi

if [ -n "$LOGOFF" ] ; then
	echo "logout option present, logging off ..."
	log_off
fi
3 Likes

Great work! I’ve been down a very similar path with this script.

Updated conditions for periodic volume update and added an automation to also update the group volume to the correct value again. For every automation that sets either the individual volume sliders or the group slider, you always need to flip the respective virtual switch. Otherwise, the group volume and the individual volume sliders will get caught in a loop.

automation:

- alias: setslidergroup
  condition:
    - condition: state
      entity_id: 'input_boolean.gruppe2'
      state: 'off'
  trigger:
    - platform: state
      entity_id: input_number.alexa_wohnzimmer
    - platform: state
      entity_id: input_number.alexa_schlafzimmer
    - platform: state
      entity_id: input_number.alexa_kuche
    - platform: state
      entity_id: input_number.alexa_bad
  action:
  - service: input_boolean.turn_on
    data:
      entity_id: input_boolean.gruppe
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_gruppe
      value: >
            {% if ((states.input_number.alexa_gruppe.state | int) + (((trigger.to_state.state | int - trigger.from_state.state | int) / 4) | round)) < 0  %}
               {% set value = (0) %}
            {% else %}
              {% set value = states.input_number.alexa_gruppe.state | int + ((trigger.to_state.state | int - trigger.from_state.state | int) / 4) | round %}
            {% endif %}
            {{value}}
  - service: input_boolean.turn_off
    data:
      entity_id: input_boolean.gruppe

      
- alias: setsliderindividual
  condition:
    - condition: state
      entity_id: 'input_boolean.gruppe'
      state: 'off'
  trigger:
    - platform: state
      entity_id: input_number.alexa_gruppe
  action:
  - service: input_boolean.turn_on
    data:
      entity_id: input_boolean.gruppe2  
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_wohnzimmer
      value: >
            {% if (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) <= 0  %}
               {% set value = ((states.input_number.alexa_wohnzimmer.state | int) * ((trigger.to_state.state | int / trigger.from_state.state | int)) ) %}
            {%-elif (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) > 0 %}
               {% set value = (((trigger.to_state.state | int - trigger.from_state.state | int) / ( 100 - trigger.from_state.state | int) * (( 100 - states.input_number.alexa_wohnzimmer.state | int))) + (states.input_number.alexa_wohnzimmer.state | int)) %}
            {% endif %}
            {{value}}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_bad
      value: >
            {% if (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) <= 0  %}
               {% set value = ((states.input_number.alexa_bad.state | int) * ((trigger.to_state.state | int / trigger.from_state.state | int)) ) %}
            {%-elif (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) > 0 %}
               {% set value = (((trigger.to_state.state | int - trigger.from_state.state | int) / ( 100 - trigger.from_state.state | int) * (( 100 - states.input_number.alexa_bad.state | int))) + (states.input_number.alexa_bad.state | int)) %}
            {% endif %}
            {{value}}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_schlafzimmer
      value: >
            {% if (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) <= 0  %}
               {% set value = ((states.input_number.alexa_schlafzimmer.state | int) * ((trigger.to_state.state | int / trigger.from_state.state | int)) ) %}
            {%-elif (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) > 0 %}
               {% set value = (((trigger.to_state.state | int - trigger.from_state.state | int) / ( 100 - trigger.from_state.state | int) * (( 100 - states.input_number.alexa_schlafzimmer.state | int))) + (states.input_number.alexa_schlafzimmer.state | int)) %}
            {% endif %}
            {{value}}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_kuche
      value: >
            {% if (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) <= 0  %}
               {% set value = ((states.input_number.alexa_kuche.state | int) * ((trigger.to_state.state | int / trigger.from_state.state | int)) ) %}
            {%-elif (((trigger.to_state.state | int - trigger.from_state.state | int)) | round) > 0 %}
               {% set value = (((trigger.to_state.state | int - trigger.from_state.state | int) / ( 100 - trigger.from_state.state | int) * (( 100 - states.input_number.alexa_kuche.state | int))) + (states.input_number.alexa_kuche.state | int)) %}
            {% endif %}
            {{value}}
  - service: input_boolean.turn_off
    data:
      entity_id: input_boolean.gruppe2
      
#########################################################################################################################################################

- alias: VolumecontrolWohnzimmer
  trigger:
    - platform: state
      entity_id: input_number.alexa_wohnzimmer
  action:
  - service: shell_command.volume
    data_template:
      level: -d Wohnzimmer -e vol:{{states.input_number.alexa_wohnzimmer.state | int}}

- alias: VolumecontrolSchlafzimmer
  trigger:
    - platform: state
      entity_id: input_number.alexa_schlafzimmer
  action:
  - service: shell_command.volume
    data_template:
      level: -d Schlafzimmer -e vol:{{states.input_number.alexa_schlafzimmer.state | int}}
      
- alias: VolumecontrolKuche
  trigger:
    - platform: state
      entity_id: input_number.alexa_kuche
  action:
  - service: shell_command.volume
    data_template:
      level: -d Küche -e vol:{{(states.input_number.alexa_kuche.state | int / 2) | int }}    

- alias: VolumecontrolBad
  trigger:
    - platform: state
      entity_id: input_number.alexa_bad
  action:
  - service: shell_command.volume
    data_template:
      level: -d Bad -e vol:{{states.input_number.alexa_bad.state | int}}   
      
#################################################  VOLUME CONTROL UPDATE ###################

- alias: UpdateVolumeSystemstart
  trigger:
    - platform: homeassistant
      event: start
  action:
  - delay: 00:00:20
  - service: input_boolean.turn_on
    data:
      entity_id: input_boolean.gruppe2
  - service: shell_command.volumeupdate
  - service: input_boolean.turn_off
    data:
      entity_id: input_boolean.gruppe2
  - delay: 00:00:05
  - service: input_boolean.turn_on
    data:
      entity_id: input_boolean.gruppe
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_gruppe
      value: '{{(((states.input_number.alexa_wohnzimmer.state | int + states.input_number.alexa_schlafzimmer.state | int + states.input_number.alexa_kuche.state | int +states.input_number.alexa_bad.state | int ) / 4) | int )}}'
  - service: input_boolean.turn_off
    data:
      entity_id: input_boolean.gruppe
###############################################  periodicly update if home, tag/nacht & spotify is playing 
- alias: UpdateVolumeperiodicly
  trigger:
    - platform: time
      minutes: '/10'
      seconds: 0
  condition:
    condition: and
    conditions:
      - condition: template
        value_template: "{% if is_state('media_player.spotify', 'playing') %}true{% endif %}"
      - condition: state
        entity_id: device_tracker.marcus_marcus
        state: 'home'
      - condition: template
        value_template: "{% if not is_state('input_select.mode', 'Away') %}true{% endif %}"      
  action:
  - service: input_boolean.turn_on
    data:
      entity_id: input_boolean.gruppe2
  - service: shell_command.volumeupdate
  - service: input_boolean.turn_off
    data:
      entity_id: input_boolean.gruppe2
  - delay: 00:00:05
  - service: input_boolean.turn_on
    data:
      entity_id: input_boolean.gruppe
  - service: input_number.set_value
    data_template:
      entity_id: input_number.alexa_gruppe
      value: '{{(((states.input_number.alexa_wohnzimmer.state | int + states.input_number.alexa_schlafzimmer.state | int + states.input_number.alexa_kuche.state | int +states.input_number.alexa_bad.state | int ) / 4) | int )}}'
  - service: input_boolean.turn_off
    data:
      entity_id: input_boolean.gruppe

Any more info you can give on the setup on this? My connection seems fine, when I run
bash alexa_remote_control.sh -a
I get a list of devices, but if I run
bash alexa_remote_control.sh -d Garage_Dot -e pause
the response is
sending cmd:{“type”:“PauseCommand”} to dev:Garage_Dot type:A3SXXXXXXVAYF serial:GXXXXXXXXCVC
but nothing happens. (The echo is obviously playing at the time)

Try it manually… for me Alexa cannot stop or skip anymore spotify songs. Neither trough the Webinterface, or via voice commands… Using Mediacontrol directly via HomeAssistant works fine.

Hi, your works looks really great. But is also quite complex for the majority of the users. Would be really cool, if home assistant would get an out of box solution for echo integration. I am currently using the text to speach engine to notify via echo about several conditions. But this is also a bit complex to install. A custom media_player like component would be really cool.

1 Like

@lweberru working on it Alexa Media Player :slight_smile:

2 Likes

Echo Devices (Alexa) as Media Player - Testers Needed :slight_smile: