Add Alexa TTS, split main config, add bathroom window alert
This commit is contained in:
parent
4d70f40a01
commit
704fd5c199
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,6 +8,7 @@
|
||||
!.gitignore
|
||||
!*.md
|
||||
!*.skel
|
||||
!alexa_*.sh
|
||||
!/config/
|
||||
!/scripts/
|
||||
!/tileboard/
|
||||
|
964
alexa_remote_control.sh
Executable file
964
alexa_remote_control.sh
Executable file
@ -0,0 +1,964 @@
|
||||
#!/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
|
||||
# 2018-02-17: v0.8d no need to write the cookie file on every "check_status"
|
||||
# 2018-02-27: v0.8e added "lastalexa" option for HA-Bridge to send its command to a specific device
|
||||
# (Markus Wennesheimer: https://wennez.wordpress.com/light-on-with-alexa-for-each-room/)
|
||||
# 2018-02-27: v0.9 unsuccessful logins will now give a short info how to debug the login
|
||||
# 2018-03-09: v0.9a workaround for login problem, force curl to use http1.1
|
||||
# 2018-05-17: v0.9b update browser string and accept language
|
||||
# 2018-05-23: v0.9c update accept language (again)
|
||||
# 2018-06-12: v0.10 introducing TTS and more
|
||||
# (thanks to Michael Geramb and his openHAB2 Amazon Echo Control binding)
|
||||
# https://github.com/openhab/openhab2-addons/tree/master/addons/binding/org.openhab.binding.amazonechocontrol
|
||||
# (thanks to Ralf Otto for implementing this feature in this script)
|
||||
# 2018-06-13: v0.10a added album play of imported library
|
||||
# 2018-06-18: v0.10b added Alex routine execution
|
||||
# 2018-06-18: v0.10b added Alexa routine execution
|
||||
# 2019-01-22: v0.11 added repeat command, ES language options, persistent cookie for hass.io
|
||||
#
|
||||
###
|
||||
#
|
||||
# (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 and PASSWORD are set on the keys 'alexa_email' and 'alexa_password'
|
||||
# in your secrets.yaml file.
|
||||
#SECRETS_YAML='/home/homeassistant/.homeassistant/secrets.yaml'
|
||||
SECRETS_YAML='/config/secrets.yaml'
|
||||
|
||||
LANGUAGE="de-DE"
|
||||
#LANGUAGE="en-GB"
|
||||
#LANGUAGE="es-ES"
|
||||
#LANGUAGE="en-US"
|
||||
|
||||
AMAZON='amazon.de'
|
||||
#AMAZON='amazon.co.uk'
|
||||
#AMAZON='amazon.es'
|
||||
#AMAZON='amazon.com'
|
||||
|
||||
ALEXA='layla.amazon.de'
|
||||
#ALEXA='layla.amazon.co.uk'
|
||||
#ALEXA='alexa.amazon.es'
|
||||
#ALEXA='pitangui.amazon.com'
|
||||
|
||||
|
||||
# binaries
|
||||
CURL='/usr/bin/curl'
|
||||
AWK='/usr/bin/awk'
|
||||
SED='/bin/sed'
|
||||
|
||||
# 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:99.0) Gecko/20100101 Firefox/99.0'
|
||||
|
||||
# Change your TMP for Hass.io to keep cookie between reboots
|
||||
TMP="/config/tmp"
|
||||
#TMP="/tmp"
|
||||
|
||||
###########################################
|
||||
# nothing to configure below here
|
||||
#
|
||||
COOKIE="${TMP}/.alexa.cookie"
|
||||
DEVLIST="${TMP}/.alexa.devicelist.json"
|
||||
|
||||
GUIVERSION=0
|
||||
|
||||
LIST=""
|
||||
LOGOFF=""
|
||||
COMMAND=""
|
||||
TTS=""
|
||||
UTTERANCE=""
|
||||
SEQUENCECMD=""
|
||||
STATIONID=""
|
||||
QUEUE=""
|
||||
SONG=""
|
||||
ALBUM=""
|
||||
ARTIST=""
|
||||
TYPE=""
|
||||
ASIN=""
|
||||
SEEDID=""
|
||||
HIST=""
|
||||
LEMUR=""
|
||||
CHILD=""
|
||||
PLIST=""
|
||||
BLUETOOTH=""
|
||||
LASTALEXA=""
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "$0 [-d <device>|ALL] -e <pause|play|next|prev|fwd|rwd|shuffle|repeat|vol:<0-100>> |"
|
||||
echo " -b [list|<\"AA:BB:CC:DD:EE:FF\">] | -q | -r <\"station name\"|stationid> |"
|
||||
echo " -s <trackID|'Artist' 'Album'> | -t <ASIN> | -u <seedID> | -v <queueID> | -w <playlistId> |"
|
||||
echo " -i | -p | -P | -S | -a | -m <multiroom_device> [device_1 .. device_X] | -lastalexa | -l | -h"
|
||||
echo
|
||||
echo " -e : run command, additional SEQUENCECMDs:"
|
||||
echo " weather,traffic,flashbriefing,goodmorning,singasong,tellstory,speak:'<text>',automation:'<routine name>'"
|
||||
echo " -b : connect/disconnect/list bluetooth device"
|
||||
echo " -q : query queue"
|
||||
echo " -r : play tunein radio"
|
||||
echo " -s : play library track/library album"
|
||||
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 " -lastalexa : print device that received the last voice command"
|
||||
echo " -l : logoff"
|
||||
echo " -h : help"
|
||||
}
|
||||
|
||||
#
|
||||
# Read yaml file and generate variables.
|
||||
# Github driven development. https://gist.github.com/pkuczynski/8665367
|
||||
#
|
||||
parse_yaml()
|
||||
{
|
||||
local prefix=$2
|
||||
local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034')
|
||||
${SED} -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
|
||||
-e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 |
|
||||
${AWK} -F$fs '{
|
||||
indent = length($1)/2;
|
||||
vname[indent] = $2;
|
||||
for (i in vname) {if (i > indent) {delete vname[i]}}
|
||||
if (length($3) > 0) {
|
||||
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
|
||||
printf("%s%s%s=\"%s\"\n", "'$prefix'",vn, $2, $3);
|
||||
}
|
||||
}'
|
||||
}
|
||||
|
||||
eval $(parse_yaml ${SECRETS_YAML} "secrets_")
|
||||
EMAIL=${secrets_alexa_email}
|
||||
PASSWORD=${secrets_alexa_password}
|
||||
|
||||
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
|
||||
if [ "${2#-}" = "${2}" -a -n "$2" ] ; then
|
||||
ALBUM=$2
|
||||
ARTIST=$SONG
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
-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"
|
||||
;;
|
||||
-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"}'
|
||||
;;
|
||||
repeat)
|
||||
COMMAND='{"type":"RepeatCommand","repeat":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
|
||||
;;
|
||||
speak:*)
|
||||
SEQUENCECMD='Alexa.Speak'
|
||||
|
||||
TTS=$(echo ${COMMAND##*:} | ${SED} -r 's/[^-a-zA-Z0-9_.,?! ]//g' | sed 's/ /_/g')
|
||||
TTS=",\\\"textToSpeak\\\":\\\"${TTS}\\\""
|
||||
;;
|
||||
automation:*)
|
||||
SEQUENCECMD='automation'
|
||||
UTTERANCE=$(echo ${COMMAND##*:} | ${SED} -r 's/[^-a-zA-Z0-9_,?! ]//g')
|
||||
;;
|
||||
weather)
|
||||
SEQUENCECMD='Alexa.Weather.Play'
|
||||
;;
|
||||
traffic)
|
||||
SEQUENCECMD='Alexa.Traffic.Play'
|
||||
;;
|
||||
flashbriefing)
|
||||
SEQUENCECMD='Alexa.FlashBriefing.Play'
|
||||
;;
|
||||
goodmorning)
|
||||
SEQUENCECMD='Alexa.GoodMorning.Play'
|
||||
;;
|
||||
singasong)
|
||||
SEQUENCECMD='Alexa.SingASong.Play'
|
||||
;;
|
||||
tellstory)
|
||||
SEQUENCECMD='Alexa.TellStory.Play'
|
||||
;;
|
||||
"")
|
||||
;;
|
||||
*)
|
||||
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 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
|
||||
|
||||
if [ -z "$(grep ".${AMAZON}.*csrf" ${COOKIE})" ] ; then
|
||||
echo "trying to get CSRF from handlebars"
|
||||
${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}/templates/oobe/d-device-pick.handlebars > /dev/null
|
||||
fi
|
||||
|
||||
if [ -z "$(grep ".${AMAZON}.*csrf" ${COOKIE})" ] ; then
|
||||
echo "trying to get CSRF from devices-v2"
|
||||
${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/devices-v2/device?cached=false > /dev/null
|
||||
fi
|
||||
|
||||
rm -f "${TMP}/.alexa.login"
|
||||
rm -f "${TMP}/.alexa.header"
|
||||
rm -f "${TMP}/.alexa.header2"
|
||||
rm -f "${TMP}/.alexa.postdata"
|
||||
rm -f "${TMP}/.alexa.postdata2"
|
||||
}
|
||||
|
||||
#
|
||||
# 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 -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L https://${ALEXA}/api/bootstrap?version=${GUIVERSION})
|
||||
MEDIAOWNERCUSTOMERID=$(echo $AUTHSTATUS | ${SED} -r 's/^.*"customerId":"([^,]+)",.*$/\1/g')
|
||||
AUTHSTATUS=$(echo $AUTHSTATUS | ${SED} -r 's/^.*"authenticated":([^,]+),.*$/\1/g')
|
||||
|
||||
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})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
# (SequenceCommands by Michael Geramb and Ralf Otto)
|
||||
#
|
||||
run_cmd()
|
||||
{
|
||||
if [ -n "${SEQUENCECMD}" ]
|
||||
then
|
||||
if [ "${SEQUENCECMD}" = 'automation' ] ; 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/behaviors/automations" > "${TMP}/.alexa.automation"
|
||||
|
||||
AUTOMATION=$(jq --arg utterance "${UTTERANCE}" -r '.[] | select( .triggers[].payload.utterance == $utterance) | .automationId' "${TMP}/.alexa.automation")
|
||||
if [ -z "${AUTOMATION}" ] ; then
|
||||
echo "ERROR: no such utterance '${UTTERANCE}' in Alexa routines"
|
||||
rm -f "${TMP}/.alexa.automation"
|
||||
exit 1
|
||||
fi
|
||||
SEQUENCE=$(jq --arg utterance "${UTTERANCE}" -r -c '.[] | select( .triggers[].payload.utterance == $utterance) | .sequence' "${TMP}/.alexa.automation" | ${SED} 's/"/\\"/g' | ${SED} "s/ALEXA_CURRENT_DEVICE_TYPE/${DEVICETYPE}/g" | ${SED} "s/ALEXA_CURRENT_DSN/${DEVICESERIALNUMBER}/g" | ${SED} "s/ALEXA_CUSTOMER_ID/${MEDIAOWNERCUSTOMERID}/g" | ${SED} 's/ /_/g')
|
||||
rm -f "${TMP}/.alexa.automation"
|
||||
|
||||
echo "Running routine: ${UTTERANCE}"
|
||||
COMMAND="{\"behaviorId\":\"${AUTOMATION}\",\"sequenceJson\":\"${SEQUENCE}\",\"status\":\"ENABLED\"}"
|
||||
else
|
||||
echo "Sequence command: ${SEQUENCECMD}"
|
||||
COMMAND="{\"behaviorId\":\"PREVIEW\",\"sequenceJson\":\"{\\\"@type\\\":\\\"com.amazon.alexa.behaviors.model.Sequence\\\",\\\"startNode\\\":{\\\"@type\\\":\\\"com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode\\\",\\\"type\\\":\\\"${SEQUENCECMD}\\\",\\\"operationPayload\\\":{\\\"deviceType\\\":\\\"${DEVICETYPE}\\\",\\\"deviceSerialNumber\\\":\\\"${DEVICESERIALNUMBER}\\\",\\\"locale\\\":\\\"${LANGUAGE}\\\",\\\"customerId\\\":\\\"${MEDIAOWNERCUSTOMERID}\\\"${TTS}}}}\",\"status\":\"ENABLED\"}"
|
||||
fi
|
||||
|
||||
${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/behaviors/preview"
|
||||
else
|
||||
${CURL} ${OPTS} -s -b ${COOKIE} -A "${BROWSER}" -H "DNT: 1" -H "Connection: keep-alive" -L\
|
||||
-H "Content-Type: application/json; charset=UTF-8" -H "Referer: https://alexa.${AMAZON}/spa/index.html" -H "Origin: https://alexa.${AMAZON}"\
|
||||
-H "csrf: $(awk "\$0 ~/.${AMAZON}.*csrf[ \\s\\t]+/ {print \$7}" ${COOKIE})" -X POST -d ${COMMAND}\
|
||||
"https://${ALEXA}/api/np/command?deviceSerialNumber=${DEVICESERIALNUMBER}&deviceType=${DEVICETYPE}"
|
||||
fi
|
||||
}
|
||||
|
||||
#
|
||||
# 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()
|
||||
{
|
||||
if [ -z "${ALBUM}" ] ; then
|
||||
JSON="{\"trackId\":\"${SONG}\",\"playQueuePrime\":true}"
|
||||
else
|
||||
JSON="{\"albumArtistName\":\"${ARTIST}\",\"albumName\":\"${ALBUM}\"}"
|
||||
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 POST -d "${JSON}"\
|
||||
"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}"
|
||||
}
|
||||
|
||||
#
|
||||
# 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" | jq -r '.activities[0].sourceDeviceIds[0].serialNumber' | xargs -i jq -r --arg device {} '.devices[] | select( .serialNumber == $device) | .accountName' ${DEVLIST}
|
||||
# Serial number: | jq -r '.activities[0].sourceDeviceIds[0].serialNumber'
|
||||
# Device name: | jq -r '.activities[0].sourceDeviceIds[0].serialNumber' | xargs -i jq -r --arg device {} '.devices[] | select( .serialNumber == $device) | .accountName' ${DEVLIST}
|
||||
}
|
||||
|
||||
#
|
||||
# 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 "$LASTALEXA" -a -z "$BLUETOOTH" -a -z "$LEMUR" -a -z "$PLIST" -a -z "$HIST" -a -z "$SEEDID" -a -z "$ASIN" -a -z "$PRIME" -a -z "$TYPE" -a -z "$QUEUE" -a -z "$LIST" -a -z "$COMMAND" -a -z "$STATIONID" -a -z "$SONG" -a -n "$LOGOFF" ] ; then
|
||||
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 ${DEVLIST} ] ; then
|
||||
echo "device list does 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} customerid:${MEDIAOWNERCUSTOMERID}"
|
||||
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} customerid:${MEDIAOWNERCUSTOMERID}"
|
||||
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
|
||||
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
|
70
alexa_wrapper.sh
Executable file
70
alexa_wrapper.sh
Executable file
@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Amazon Alexa TTS Home Assistant Wrapper
|
||||
#
|
||||
# 2018-06-18: v0.1 initial release
|
||||
#
|
||||
# This script is intended to allow the Alexa Remote Control script
|
||||
# from Alex Lotzimmer to be used as a command line notify platform
|
||||
# in Home Assistant.
|
||||
#
|
||||
# Usage:
|
||||
# ./alexa_wrapper.sh -d "My Dot Name"
|
||||
#
|
||||
# Home Assistant will pass the message to the script via STDIN. The
|
||||
# Alexa Remote control script requires that spaces be replaced with
|
||||
# underscores.
|
||||
#
|
||||
# Installation:
|
||||
# Place alexa_wrapper.sh and alexa_remote_control.sh in your Home Assistant
|
||||
# config directory. In a shell type echo $PATH and replace the below PATH
|
||||
# variable with your values.
|
||||
#
|
||||
# Edit alexa_remote_control.sh with your credentials and
|
||||
# your location. Test that you can pull a list of devices with
|
||||
# ./alexa_remote_control.sh -a
|
||||
#
|
||||
# Add a command line notify component for each Alexa device
|
||||
# to Home Assistant as follows:
|
||||
#
|
||||
# notify:
|
||||
# - platform: command_line
|
||||
# name: 'My Dot Name'
|
||||
# command: "/home/homeassistant/.homeassistant/alexa_wrapper -d 'My Dot Name'"
|
||||
#
|
||||
# You should then be able to call notify.my_dot_name from automations
|
||||
#
|
||||
|
||||
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
ALEXA_REMOTE="$DIR/alexa_remote_control.sh"
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "$0 -d <device>|ALL"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
-d)
|
||||
if [ "${2#-}" != "${2}" -o -z "$2" ] ; then
|
||||
echo "ERROR: missing argument for ${1}"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
DEVICE=$2
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown option ${1}"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
|
||||
read message
|
||||
|
||||
formatted=${message// /_}
|
||||
|
||||
$ALEXA_REMOTE -d "$DEVICE" -e speak:$formatted >> /dev/null
|
||||
exit 0
|
@ -1,5 +0,0 @@
|
||||
#motion_battery:
|
||||
# entity_id: binary_sensor.humidity_alarm_parents_bathroom
|
||||
# repeat: 30
|
||||
# notifiers:
|
||||
# - telegram_fb
|
12
config/alerts/windows.yaml
Normal file
12
config/alerts/windows.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
bathroom_window_open:
|
||||
name: Badezimmer-Fenster geöffnet
|
||||
message: "Zur Info: Das *Badezimmer-Fenster* ist noch *offen*!"
|
||||
done_message: "Zur Info: Das *Badezimmer-Fenster* ist wieder *geschlossen*."
|
||||
entity_id: binary_sensor.lumi_bathroom_window_magnet
|
||||
state: "on"
|
||||
repeat: 10
|
||||
can_acknowledge: false
|
||||
skip_first: true
|
||||
notifiers:
|
||||
- telegram_group
|
||||
- alexa_kitchen
|
@ -1,31 +0,0 @@
|
||||
- alias: Dachboden Luftfeuchtigkeit
|
||||
trigger:
|
||||
platform: template
|
||||
value_template: "{% if (states.sensor.attic_humidity.state|float) > 64 or (states.sensor.attic_humidity.state|float) < 35 %}true{% endif %}"
|
||||
action:
|
||||
service: notify.android
|
||||
data:
|
||||
title: 'Bitte den *Dachboden* lüften!'
|
||||
message: 'Die Luftfeuchtigkeit liegt mit einem Wert von *{{ states.sensor.attic_humidity.state }}%* außerhalb des Grenzbereichs.'
|
||||
|
||||
#- alias: Badezimmer Luftfeuchtigkeit
|
||||
# trigger:
|
||||
# platform: numeric_state
|
||||
# entity_id: sensor.bathroom_hygrometer_humidity
|
||||
# value_template: '{{ state|float }}'
|
||||
# below: 35
|
||||
# above: 65
|
||||
# action:
|
||||
# service: notify.telegram_group
|
||||
# data:
|
||||
# title: 'Bitte das Badezimmer lüften!'
|
||||
# message: 'Die Luftfeuchtigkeit liegt mit {{ states.sensor.bathroom_hygrometer_humidity.state }}% außerhalb des Grenzbereichs.'
|
||||
- alias: Badezimmer Luftfeuchtigkeit
|
||||
trigger:
|
||||
platform: template
|
||||
value_template: "{% if states.sensor.bathroom_hygrometer_humidity and ((states.sensor.bathroom_hygrometer_humidity.state|float) > 55 or (states.sensor.bathroom_hygrometer_humidity.state|float) < 35) %}true{% endif %}"
|
||||
action:
|
||||
service: notify.telegram_group
|
||||
data:
|
||||
title: 'Bitte das *Badezimmer* lüften!'
|
||||
message: 'Die Luftfeuchtigkeit liegt mit einem Wert von *{{ states.sensor.bathroom_hygrometer_humidity.state }}%* außerhalb des Grenzbereichs.'
|
9
config/cameras.yaml
Normal file
9
config/cameras.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
- platform: reolink_dev
|
||||
host: !secret cam_livingroom_ip
|
||||
username: !secret cam_livingroom_user
|
||||
password: !secret cam_livingroom_password
|
||||
name: livingroom
|
||||
stream: main
|
||||
protocol: rtmp
|
||||
channel: 0
|
||||
scan_interval: 30
|
51
config/influxdb.yaml
Normal file
51
config/influxdb.yaml
Normal file
@ -0,0 +1,51 @@
|
||||
host: !secret influxdb
|
||||
database: home_assistant
|
||||
username: !secret influxdb_user
|
||||
password: !secret influxdb_password
|
||||
max_retries: 3
|
||||
include:
|
||||
entities:
|
||||
- sensor.hygro_bathroom_parents_humidity
|
||||
- sensor.hygro_bathroom_parents_temperature
|
||||
- sensor.hygro_bathroom_kids_humidity
|
||||
- sensor.hygro_bathroom_kids_temperature
|
||||
- sensor.humidity_4 # Wohnzimmer
|
||||
- sensor.temperature_3 # Wohnzimmer
|
||||
- sensor.humidity_15 # Büro
|
||||
- sensor.temperature_14 # Büro
|
||||
- sensor.attic_humidity_2
|
||||
- sensor.attic_temperature_2
|
||||
# Youtube
|
||||
- sensor.youtube_beauty
|
||||
- sensor.youtube_beauty_videos
|
||||
- sensor.youtube_beauty_views
|
||||
- sensor.youtube_mtb
|
||||
- sensor.youtube_mtb_videos
|
||||
- sensor.youtube_mtb_views
|
||||
# Bitly
|
||||
- sensor.bitly_blog_bb
|
||||
- sensor.bitly_instagram_bb
|
||||
- sensor.bitly_impressum_bb
|
||||
- sensor.bitly_youtube_bb
|
||||
# Instagram
|
||||
- sensor.instagram_beauty
|
||||
- sensor.instagram_beauty_follows
|
||||
- sensor.instagram_beauty_media
|
||||
- sensor.instagram_fb
|
||||
- sensor.instagram_fb_follows
|
||||
- sensor.instagram_fb_media
|
||||
- sensor.instagram_franky
|
||||
- sensor.instagram_franky_follows
|
||||
- sensor.instagram_franky_media
|
||||
- sensor.instagram_mtb
|
||||
- sensor.instagram_mtb_follows
|
||||
- sensor.instagram_mtb_media
|
||||
- sensor.instagram_medieval
|
||||
- sensor.instagram_medieval_follows
|
||||
- sensor.instagram_medieval_media
|
||||
- sensor.instagram_lotte
|
||||
- sensor.instagram_lotte_follows
|
||||
- sensor.instagram_lotte_media
|
||||
- sensor.instagram_codedwithlove
|
||||
- sensor.instagram_codedwithlove_follows
|
||||
- sensor.instagram_codedwithlove_media
|
7
config/lights.yaml
Normal file
7
config/lights.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
- platform: hyperion
|
||||
name: Ambilight
|
||||
host: !secret ambilight_ip
|
||||
hdmi_priority: 900
|
||||
- platform: switch
|
||||
name: Stimmungslicht
|
||||
entity_id: switch.livingroom_stimmungslicht
|
@ -1,6 +1,3 @@
|
||||
dummy:
|
||||
sequence:
|
||||
|
||||
ambilight_off:
|
||||
alias: Ambilight schwarz schalten
|
||||
sequence:
|
||||
@ -123,26 +120,4 @@ harmony_volume_down:
|
||||
command:
|
||||
- VolumeDown
|
||||
device: 42849850 #Denon
|
||||
num_repeats: 5
|
||||
|
||||
# Wallboard
|
||||
|
||||
wallboard_hdmi_on:
|
||||
sequence:
|
||||
- service: shell_command.ssh
|
||||
data:
|
||||
sshkey: !secret sshkey_wallboard_hdmi
|
||||
host: !secret wallboard_ip
|
||||
user: !secret wallboard_ssh_user
|
||||
port: !secret wallboard_ssh_port
|
||||
command_or_param: 1
|
||||
|
||||
wallboard_hdmi_off:
|
||||
sequence:
|
||||
- service: shell_command.ssh
|
||||
data:
|
||||
sshkey: !secret sshkey_wallboard_hdmi
|
||||
host: !secret wallboard_ip
|
||||
user: !secret wallboard_ssh_user
|
||||
port: !secret wallboard_ssh_port
|
||||
command_or_param: 0
|
||||
num_repeats: 5
|
2
config/scripts/misc.yaml
Normal file
2
config/scripts/misc.yaml
Normal file
@ -0,0 +1,2 @@
|
||||
dummy:
|
||||
sequence:
|
19
config/scripts/wallboard.yaml
Normal file
19
config/scripts/wallboard.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
wallboard_hdmi_on:
|
||||
sequence:
|
||||
- service: shell_command.ssh
|
||||
data:
|
||||
sshkey: !secret sshkey_wallboard_hdmi
|
||||
host: !secret wallboard_ip
|
||||
user: !secret wallboard_ssh_user
|
||||
port: !secret wallboard_ssh_port
|
||||
command_or_param: 1
|
||||
|
||||
wallboard_hdmi_off:
|
||||
sequence:
|
||||
- service: shell_command.ssh
|
||||
data:
|
||||
sshkey: !secret sshkey_wallboard_hdmi
|
||||
host: !secret wallboard_ip
|
||||
user: !secret wallboard_ssh_user
|
||||
port: !secret wallboard_ssh_port
|
||||
command_or_param: 0
|
9
config/sensors_binary/cctv.yaml
Normal file
9
config/sensors_binary/cctv.yaml
Normal file
@ -0,0 +1,9 @@
|
||||
- platform: template
|
||||
sensors:
|
||||
motion_livingroom:
|
||||
friendly_name: Motion Livingroom
|
||||
device_class: motion
|
||||
entity_id: camera.livingroom
|
||||
value_template: "{{ is_state('camera.livingroom', 'motion') }}"
|
||||
delay_off:
|
||||
seconds: 30
|
19
config/sensors_binary/misc.yaml
Normal file
19
config/sensors_binary/misc.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
- platform: workday
|
||||
country: DE
|
||||
province: NW
|
||||
workdays: [ mon, tue, wed, thu, fri ]
|
||||
- platform: tod
|
||||
name: Morning
|
||||
after: '05:00'
|
||||
before: '11:00'
|
||||
- platform: ping
|
||||
name: desktop_ping
|
||||
host: !secret desktop_ip
|
||||
count: 2
|
||||
scan_interval: 15
|
||||
- platform: template
|
||||
sensors:
|
||||
anyone_home:
|
||||
value_template: >-
|
||||
{{ is_state('person.florian', 'home')
|
||||
or is_state('person.jenny', 'home') }}
|
6
config/sensors_binary/wallboard.yaml
Normal file
6
config/sensors_binary/wallboard.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
- platform: command_line
|
||||
name: Wallboard HDMI Status
|
||||
command: !secret wallboard_hdmi_status_cmd
|
||||
payload_on: display_power=1
|
||||
payload_off: display_power=0
|
||||
scan_interval: 60
|
53
config/switches/cctv.yaml
Normal file
53
config/switches/cctv.yaml
Normal file
@ -0,0 +1,53 @@
|
||||
- platform: template
|
||||
switches:
|
||||
|
||||
camera_livingroom_email:
|
||||
value_template: "{{ is_state_attr('camera.livingroom', 'email_enabled', true) }}"
|
||||
turn_on:
|
||||
service: camera.enable_email
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
turn_off:
|
||||
service: camera.disable_email
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
icon_template: >-
|
||||
{% if is_state_attr('camera.livingroom', 'email_enabled', true) %}
|
||||
mdi:email
|
||||
{% else %}
|
||||
mdi:email-outline
|
||||
{% endif %}
|
||||
|
||||
camera_livingroom_ftp:
|
||||
value_template: "{{ is_state_attr('camera.livingroom', 'ftp_enabled', true) }}"
|
||||
turn_on:
|
||||
service: camera.enable_ftp
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
turn_off:
|
||||
service: camera.disable_ftp
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
icon_template: >-
|
||||
{% if is_state_attr('camera.livingroom', 'ftp_enabled', true) %}
|
||||
mdi:filmstrip
|
||||
{% else %}
|
||||
mdi:filmstrip-off
|
||||
{% endif %}
|
||||
|
||||
camera_livingroom_ir_lights:
|
||||
value_template: "{{ is_state_attr('camera.livingroom', 'ir_lights_enabled', true) }}"
|
||||
turn_on:
|
||||
service: camera.enable_ir_lights
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
turn_off:
|
||||
service: camera.disable_ir_lights
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
icon_template: >-
|
||||
{% if is_state_attr('camera.livingroom', 'ir_lights_enabled', true) %}
|
||||
mdi:flashlight
|
||||
{% else %}
|
||||
mdi:flashlight-off
|
||||
{% endif %}
|
@ -1,17 +1,5 @@
|
||||
- platform: template
|
||||
switches:
|
||||
desktop_wol:
|
||||
value_template: "{{ is_state('binary_sensor.desktop_ping', 'on') }}"
|
||||
turn_on:
|
||||
service: shell_command.ssh
|
||||
data:
|
||||
sshkey: !secret sshkey_wakeonlan
|
||||
host: !secret nas_ip
|
||||
user: !secret nas_ssh_user
|
||||
port: !secret nas_ssh_port
|
||||
command_or_param: !secret desktop_mac
|
||||
turn_off:
|
||||
service: script.dummy
|
||||
|
||||
harmony_music_bt:
|
||||
value_template: "{{ is_state_attr('remote.livingroom_harmony', 'current_activity', 'Musik Bluetooth') }}"
|
||||
@ -109,67 +97,4 @@
|
||||
service: remote.turn_on
|
||||
data:
|
||||
entity_id: remote.livingroom_harmony
|
||||
activity: 'PowerOff'
|
||||
|
||||
### CCTV
|
||||
|
||||
camera_livingroom_email:
|
||||
value_template: "{{ is_state_attr('camera.livingroom', 'email_enabled', true) }}"
|
||||
turn_on:
|
||||
service: camera.enable_email
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
turn_off:
|
||||
service: camera.disable_email
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
icon_template: >-
|
||||
{% if is_state_attr('camera.livingroom', 'email_enabled', true) %}
|
||||
mdi:email
|
||||
{% else %}
|
||||
mdi:email-outline
|
||||
{% endif %}
|
||||
|
||||
camera_livingroom_ftp:
|
||||
value_template: "{{ is_state_attr('camera.livingroom', 'ftp_enabled', true) }}"
|
||||
turn_on:
|
||||
service: camera.enable_ftp
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
turn_off:
|
||||
service: camera.disable_ftp
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
icon_template: >-
|
||||
{% if is_state_attr('camera.livingroom', 'ftp_enabled', true) %}
|
||||
mdi:filmstrip
|
||||
{% else %}
|
||||
mdi:filmstrip-off
|
||||
{% endif %}
|
||||
|
||||
camera_livingroom_ir_lights:
|
||||
value_template: "{{ is_state_attr('camera.livingroom', 'ir_lights_enabled', true) }}"
|
||||
turn_on:
|
||||
service: camera.enable_ir_lights
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
turn_off:
|
||||
service: camera.disable_ir_lights
|
||||
data:
|
||||
entity_id: camera.livingroom
|
||||
icon_template: >-
|
||||
{% if is_state_attr('camera.livingroom', 'ir_lights_enabled', true) %}
|
||||
mdi:flashlight
|
||||
{% else %}
|
||||
mdi:flashlight-off
|
||||
{% endif %}
|
||||
|
||||
### Wallboard
|
||||
|
||||
wallboard_display:
|
||||
friendly_name: Wallboard Display Toggle
|
||||
value_template: "{{ is_state('binary_sensor.wallboard_hdmi_status', 'on') }}"
|
||||
turn_on:
|
||||
service: script.wallboard_hdmi_on
|
||||
turn_off:
|
||||
service: script.wallboard_hdmi_off
|
||||
activity: 'PowerOff'
|
15
config/switches/misc.yaml
Normal file
15
config/switches/misc.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
- platform: template
|
||||
switches:
|
||||
|
||||
desktop_wol:
|
||||
value_template: "{{ is_state('binary_sensor.desktop_ping', 'on') }}"
|
||||
turn_on:
|
||||
service: shell_command.ssh
|
||||
data:
|
||||
sshkey: !secret sshkey_wakeonlan
|
||||
host: !secret nas_ip
|
||||
user: !secret nas_ssh_user
|
||||
port: !secret nas_ssh_port
|
||||
command_or_param: !secret desktop_mac
|
||||
turn_off:
|
||||
service: script.dummy
|
10
config/switches/wallboard.yaml
Normal file
10
config/switches/wallboard.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
- platform: template
|
||||
switches:
|
||||
|
||||
wallboard_display:
|
||||
friendly_name: Wallboard Display Toggle
|
||||
value_template: "{{ is_state('binary_sensor.wallboard_hdmi_status', 'on') }}"
|
||||
turn_on:
|
||||
service: script.wallboard_hdmi_on
|
||||
turn_off:
|
||||
service: script.wallboard_hdmi_off
|
@ -15,8 +15,19 @@ homeassistant:
|
||||
- 192.168.0.0/16
|
||||
- fd00::/8
|
||||
|
||||
frontend:
|
||||
themes: !include_dir_merge_named themes/
|
||||
config:
|
||||
conversation:
|
||||
device_tracker:
|
||||
#discovery:
|
||||
history:
|
||||
logbook:
|
||||
map:
|
||||
mobile_app:
|
||||
recorder:
|
||||
sun:
|
||||
stream:
|
||||
system_health:
|
||||
updater:
|
||||
|
||||
http:
|
||||
ip_ban_enabled: true
|
||||
@ -26,100 +37,16 @@ http:
|
||||
- 127.0.0.1
|
||||
- ::1
|
||||
|
||||
frontend:
|
||||
themes: !include_dir_merge_named themes/
|
||||
|
||||
lovelace:
|
||||
mode: yaml
|
||||
|
||||
zone:
|
||||
- name: !secret work_name_f
|
||||
latitude: !secret work_lat_f
|
||||
longitude: !secret work_long_f
|
||||
radius: 250
|
||||
icon: mdi:briefcase
|
||||
- name: !secret work_name_j
|
||||
latitude: !secret work_lat_j
|
||||
longitude: !secret work_long_j
|
||||
radius: 500
|
||||
icon: mdi:briefcase
|
||||
|
||||
system_health:
|
||||
|
||||
config:
|
||||
|
||||
updater:
|
||||
|
||||
#discovery:
|
||||
|
||||
conversation:
|
||||
|
||||
tts:
|
||||
- platform: google_translate
|
||||
service_name: google_say
|
||||
|
||||
history:
|
||||
|
||||
logbook:
|
||||
|
||||
map:
|
||||
|
||||
recorder:
|
||||
|
||||
influxdb:
|
||||
host: !secret influxdb
|
||||
database: home_assistant
|
||||
username: !secret influxdb_user
|
||||
password: !secret influxdb_password
|
||||
max_retries: 3
|
||||
include:
|
||||
entities:
|
||||
- sensor.hygro_bathroom_parents_humidity
|
||||
- sensor.hygro_bathroom_parents_temperature
|
||||
- sensor.hygro_bathroom_kids_humidity
|
||||
- sensor.hygro_bathroom_kids_temperature
|
||||
- sensor.humidity_4 # Wohnzimmer
|
||||
- sensor.temperature_3 # Wohnzimmer
|
||||
- sensor.humidity_15 # Büro
|
||||
- sensor.temperature_14 # Büro
|
||||
- sensor.attic_humidity_2
|
||||
- sensor.attic_temperature_2
|
||||
# Youtube
|
||||
- sensor.youtube_beauty
|
||||
- sensor.youtube_beauty_videos
|
||||
- sensor.youtube_beauty_views
|
||||
- sensor.youtube_mtb
|
||||
- sensor.youtube_mtb_videos
|
||||
- sensor.youtube_mtb_views
|
||||
# Bitly
|
||||
- sensor.bitly_blog_bb
|
||||
- sensor.bitly_instagram_bb
|
||||
- sensor.bitly_impressum_bb
|
||||
- sensor.bitly_youtube_bb
|
||||
# Instagram
|
||||
- sensor.instagram_beauty
|
||||
- sensor.instagram_beauty_follows
|
||||
- sensor.instagram_beauty_media
|
||||
- sensor.instagram_fb
|
||||
- sensor.instagram_fb_follows
|
||||
- sensor.instagram_fb_media
|
||||
- sensor.instagram_franky
|
||||
- sensor.instagram_franky_follows
|
||||
- sensor.instagram_franky_media
|
||||
- sensor.instagram_mtb
|
||||
- sensor.instagram_mtb_follows
|
||||
- sensor.instagram_mtb_media
|
||||
- sensor.instagram_medieval
|
||||
- sensor.instagram_medieval_follows
|
||||
- sensor.instagram_medieval_media
|
||||
- sensor.instagram_lotte
|
||||
- sensor.instagram_lotte_follows
|
||||
- sensor.instagram_lotte_media
|
||||
- sensor.instagram_codedwithlove
|
||||
- sensor.instagram_codedwithlove_follows
|
||||
- sensor.instagram_codedwithlove_media
|
||||
|
||||
#deconz:
|
||||
# host: !secret deconz_ip
|
||||
# port: !secret deconz_port
|
||||
|
||||
zha:
|
||||
usb_path: /dev/ttyACM0
|
||||
radio_type: deconz
|
||||
@ -142,8 +69,6 @@ mqtt_eventstream:
|
||||
- call_service
|
||||
- state_changed
|
||||
|
||||
mobile_app:
|
||||
|
||||
telegram_bot:
|
||||
- platform: broadcast
|
||||
api_key: !secret telegram_bot
|
||||
@ -159,6 +84,15 @@ notify:
|
||||
- name: telegram_fb
|
||||
platform: telegram
|
||||
chat_id: !secret telegram_chat_fb
|
||||
- platform: command_line
|
||||
name: alexa_kitchen
|
||||
command: "/config/alexa_wrapper.sh -d 'Küche'"
|
||||
- platform: command_line
|
||||
name: alexa_livingroom
|
||||
command: "/config/alexa_wrapper.sh -d 'Wohnzimmer'"
|
||||
- platform: command_line
|
||||
name: alexa_all
|
||||
command: "/config/alexa_wrapper.sh -d 'ALL'"
|
||||
# - name: android
|
||||
# platform: fcm-android
|
||||
|
||||
@ -179,8 +113,6 @@ remote:
|
||||
host: !secret harmonyhub_ip
|
||||
delay_secs: 0.3
|
||||
|
||||
sun:
|
||||
|
||||
person:
|
||||
- name: Jenny
|
||||
id: jenny
|
||||
@ -190,52 +122,17 @@ person:
|
||||
- device_tracker.fb_mobile_bt
|
||||
- device_tracker.mobile_fb
|
||||
|
||||
device_tracker:
|
||||
|
||||
light:
|
||||
- platform: hyperion
|
||||
name: Ambilight
|
||||
host: !secret ambilight_ip
|
||||
hdmi_priority: 900
|
||||
- platform: switch
|
||||
name: Stimmungslicht
|
||||
entity_id: switch.livingroom_stimmungslicht
|
||||
|
||||
binary_sensor:
|
||||
- platform: workday
|
||||
country: DE
|
||||
province: NW
|
||||
workdays: [ mon, tue, wed, thu, fri ]
|
||||
- platform: tod
|
||||
name: Morning
|
||||
after: '05:00'
|
||||
before: '11:00'
|
||||
- platform: ping
|
||||
name: desktop_ping
|
||||
host: !secret desktop_ip
|
||||
count: 2
|
||||
scan_interval: 15
|
||||
- platform: command_line
|
||||
name: Wallboard HDMI Status
|
||||
command: !secret wallboard_hdmi_status_cmd
|
||||
payload_on: display_power=1
|
||||
payload_off: display_power=0
|
||||
scan_interval: 60
|
||||
- platform: template
|
||||
sensors:
|
||||
anyone_home:
|
||||
value_template: >-
|
||||
{{ is_state('person.florian', 'home')
|
||||
or is_state('person.jenny', 'home') }}
|
||||
- platform: template
|
||||
sensors:
|
||||
motion_livingroom:
|
||||
friendly_name: Motion Livingroom
|
||||
device_class: motion
|
||||
entity_id: camera.livingroom
|
||||
value_template: "{{ is_state('camera.livingroom', 'motion') }}"
|
||||
delay_off:
|
||||
seconds: 30
|
||||
zone:
|
||||
- name: !secret work_name_f
|
||||
latitude: !secret work_lat_f
|
||||
longitude: !secret work_long_f
|
||||
radius: 250
|
||||
icon: mdi:briefcase
|
||||
- name: !secret work_name_j
|
||||
latitude: !secret work_lat_j
|
||||
longitude: !secret work_long_j
|
||||
radius: 500
|
||||
icon: mdi:briefcase
|
||||
|
||||
weather:
|
||||
- platform: openweathermap
|
||||
@ -244,19 +141,6 @@ weather:
|
||||
ffmpeg:
|
||||
ffmpeg_bin: /usr/bin/ffmpeg
|
||||
|
||||
stream:
|
||||
|
||||
camera:
|
||||
- platform: reolink_dev
|
||||
host: !secret cam_livingroom_ip
|
||||
username: !secret cam_livingroom_user
|
||||
password: !secret cam_livingroom_password
|
||||
name: livingroom
|
||||
stream: main
|
||||
protocol: rtmp
|
||||
channel: 0
|
||||
scan_interval: 30
|
||||
|
||||
input_datetime:
|
||||
bedroom_alarm_clock_time:
|
||||
name: Wecker
|
||||
@ -303,10 +187,15 @@ tplink:
|
||||
switch:
|
||||
- host: !secret tplink_ip
|
||||
|
||||
alert: !include_dir_merge_list config/alerts/
|
||||
# External config files
|
||||
alert: !include_dir_merge_named config/alerts/
|
||||
alexa: !include config/alexa.yaml
|
||||
automation: !include_dir_merge_list config/automations/
|
||||
binary_sensor: !include_dir_merge_list config/sensors_binary/
|
||||
camera: !include config/cameras.yaml
|
||||
influxdb: !include config/influxdb.yaml
|
||||
light: !include config/lights.yaml
|
||||
scene: !include_dir_merge_list config/scenes/
|
||||
script: !include config/scripts.yaml
|
||||
script: !include_dir_merge_list config/scripts/
|
||||
sensor: !include_dir_merge_list config/sensors/
|
||||
switch: !include config/switches.yaml
|
||||
switch: !include_dir_merge_list config/switches/
|
Loading…
Reference in New Issue
Block a user