Home "automation" in Linux with netcat/socat
I realize that this post i lacking in instructions. I might flesh this out at a later date but no guaranties. It’s not really that difficult once you figure out my madness ;)
I’m using tasker and udp sender to send udp packets with commands to a listening socat.
So if I send, for example (trailing blank line is intentional):
hue_on
kodi_on
… my Hue lights and HTPC will turn on. Tasker is then set up to send these commands when my phone connects to my home network. Simple and effective.
On to the script.
Please note: The tdtool-lines you see in the case file is a tool for Tellstick which can be used to toggle power switches on and off remotely.
The ssh command requires you to have set up ssh keys to automate ssh logins.
Kodi stuff requires that your Kodi installation is set up to accept calls to its JSON-RPC API._
With that out of the way …
The script is actually made up of at least three files.
One containing variables and functions (sourced in main script as *.fv):
### Variables
htpc_name=user@computername
kodi_json=http://computername:8080/jsonrpc
kodi_mac=00:00:00:00:00:00
hue_bridge=0.0.0.0
hue_apikey=1234567890
hue_lights=1,2,3,4,5
rc_ip=0.0.0.0
### Functions
doKodi() {
curl -s --data-binary "{ \"jsonrpc\": \"2.0\", \"method\": $1,\"params\":{$2} , \"id\": \"mybash\" }" \
-H 'content-type: application/json;' $kodi_json
}
doHue() {
curl -s -X PUT -H "Content-Type: application/json" -d \
"{ \"ct\":600,\"bri\":254,\"on\":$2,\"transitiontime\":100 }" \
http://$hue_bridge/api/$hue_apikey/lights/$1/state
}
doLX() {
telnet $rc_ip 2> /dev/null
}
doHTPC() {
ssh -n -f $htpc_name "sh -c 'nohup $1 &> /dev/null &'"
}
One, or more, containing the case statement (sourced in main script as *.do):
# Changes to this file does NOT require reload of main script.
# Note that line endings must be LF or bash gets confused and cranky.
case "$1" in
### DAC
dac_on)
echo DAC: on
tdtool -n 1
;;
dac_off)
echo DAC: off
tdtool -f 1
;;
### FAN
fan_on)
echo FAN: on
tdtool -n 2
;;
fan_off)
echo FAN: off
tdtool -f 2
;;
### Reciever
rc_on)
echo Reciever: on
{ sleep 0.1; echo "PO"; sleep 6; echo "01FN"; sleep 2; } | doLX
;;
rc_off)
echo Reciever: off
{ sleep 0.1; echo "25FN"; sleep 2; } | doLX
;;
### KODI
dlna_on)
echo Kodi: DLNA on
doKodi '"Input.Home"' ''
sleep 1
doKodi '"Profiles.LoadProfile"' '"profile":"optical"'
;;
dlna_off)
echo Kodi: DLNA off
doKodi '"Input.Home"' ''
sleep 1
doKodi '"Profiles.LoadProfile"' '"profile":"Master user"'
;;
kodi_on)
echo Kodi: system on
wol $kodi_mac
;;
kodi_off)
echo Kodi: system off
doKodi '"Input.Home"' ''
sleep 1
doKodi '"System.Shutdown"' ''
;;
### HUE
hue_on)
echo HUE: all on
for x in $hue_lights; do
doHue $x true &
sleep .3
done
;;
hue_off)
echo HUE: all off
for x in $hue_lights; do
doHue $x false &
sleep .3
done
;;
### HTPC
spotify_on)
echo Spotify: Start
doHTPC "start_spotify.sh"
;;
spotify_off)
echo Spotify: Stop
doHTPC "pkill Spotify"
;;
esac
And finally the main script tying it all together:
#!/bin/bash
### Figure out who we are
me=$(basename $0)
### Figure out where we are
dir="${BASH_SOURCE%/*}"
if [[ ! -d "$dir" ]]; then dir="$PWD"; fi
### Figure out where to talk
logfile=$dir/$me.log
#touch $logfile
echo > $logfile
### Figure out where to listen
socat_port=2222
### Figure out how to do
# Sourcing functions and variables from .fv files for readability
# Changes to .fv-files requires reload of main script.
. $dir/*.fv
### Go, do.
doFluff() {
# tell bash to separate on comma
IFS=','
### Figure out what to do ...
# Sourcing cases from .do files for readability.
# If a incomming call is present in more than one file,
# both commands will be executed.
# Changes to .do-files does NOT require reload of main script.
for f in $dir/*.do; do
. $f
done
unset IFS # restore IFS to default
}
### Right round, baby, right round.
loop() {
socat udp-listen:$socat_port,reuseaddr,fork - | while read do; do
local d=$(date +"%F %T")
echo -n $d >> $logfile
echo " :: do: $do" >> $logfile
doFluff $do >> $logfile
echo >> $logfile
done
}
killtree() {
local pid=$1 child
for child in $(pgrep -P $pid); do
killtree $child
done
[ $pid -ne $$ ] && kill -kill $pid
}
### If we are already alive, kill us ...
if [ $(pgrep -c $me) -gt 1 ]; then
echo " Killing previous instance(s) of $me"
#pkill -of $me
pids=($(pgrep "$me"))
for i in "${pids[@]}"
do
#(( $$ != i)) && kill -- -"$i"
#(( $$ != i)) && kill -- -$(ps -o pgid=$i | grep -o [0-9]\*)
killtree $i &> /dev/null
done
fi
### then rise again ...
loop &
### and fade away.
echo " Loop engaged, script backgrounded."
exit 0