Skip to content

Instantly share code, notes, and snippets.

@rringler
Created August 27, 2017 20:12
Show Gist options
  • Save rringler/35ffa241b4d7a8a2e08dde7543fae55c to your computer and use it in GitHub Desktop.
Save rringler/35ffa241b4d7a8a2e08dde7543fae55c to your computer and use it in GitHub Desktop.
Shell Script to control a TP-Link HS100 Smart Outlet
#!/bin/bash
##
# Controls TP-LINK HS100,HS110, HS200 wlan smart plugs
# Tested with HS100 firmware 1.0.8
#
# Credits to Thomas Baust for the query/status/emeter commands
#
# Author George Georgovassilis, https://github.com/ggeorgovassilis/linuxscripts
ip=$1
port=$2
cmd=$3
# encoded (the reverse of decode) commands to send to the plug
# encoded {"system":{"set_relay_state":{"state":1}}}
payload_on="AAAAKtDygfiL/5r31e+UtsWg1Iv5nPCR6LfEsNGlwOLYo4HyhueT9tTu36Lfog=="
# encoded {"system":{"set_relay_state":{"state":0}}}
payload_off="AAAAKtDygfiL/5r31e+UtsWg1Iv5nPCR6LfEsNGlwOLYo4HyhueT9tTu3qPeow=="
# encoded { "system":{ "get_sysinfo":null } }
payload_query="AAAAI9Dw0qHYq9+61/XPtJS20bTAn+yV5o/hh+jK8J7rh+vLtpbr"
# the encoded request { "emeter":{ "get_realtime":null } }
payload_emeter="AAAAJNDw0rfav8uu3P7Ev5+92r/LlOaD4o76k/6buYPtmPSYuMXlmA=="
# tools
check_dependencies() {
command -v nc >/dev/null 2>&1 || { echo >&2 "The nc programme for sending data over the network isn't in the path, communication with the plug will fail"; exit 2; }
command -v base64 >/dev/null 2>&1 || { echo >&2 "The base64 programme for decoding base64 encoded strings isn't in the path, decoding of payloads will fail"; exit 2; }
command -v od >/dev/null 2>&1 || { echo >&2 "The od programme for converting binary data to numbers isn't in the path, the status and emeter commands will fail";}
command -v read >/dev/null 2>&1 || { echo >&2 "The read programme for splitting text into tokens isn't in the path, the status and emeter commands will fail";}
command -v printf >/dev/null 2>&1 || { echo >&2 "The printf programme for converting numbers into binary isn't in the path, the status and emeter commands will fail";}
}
show_usage() {
echo Usage: $0 IP PORT COMMAND
echo where COMMAND is one of on/off/check/status/emeter/toggle
exit 1
}
check_arguments() {
check_arg() {
name="$1"
value="$2"
if [ -z "$value" ]; then
echo "missing argument $name"
show_usage
fi
}
check_arg "ip" $ip
check_arg "port" $port
check_arg "command" $cmd
}
send_to_plug() {
ip="$1"
port="$2"
payload="$3"
echo -n "$payload" | base64 --decode | nc -v $ip $port || echo couldn''t connect to $ip:$port, nc failed with exit code $?
}
decode(){
code=171
offset=4
input_num=`od -j $offset -An -t u1 -v | tr "\n" " "`
IFS=' ' read -r -a array <<< "$input_num"
args_for_printf=""
for element in "${array[@]}"
do
output=$(( $element ^ $code ))
args_for_printf="$args_for_printf\x$(printf %x $output)"
code=$element
done
printf "$args_for_printf"
}
query_plug(){
payload=$1
send_to_plug $ip $port "$payload" | decode
}
# plug commands
cmd_print_plug_relay_state(){
output=`send_to_plug $ip $port "$payload_query" | decode | egrep -o 'relay_state":[0,1]' | egrep -o '[0,1]'`
if [[ $output -eq 0 ]]; then
echo OFF
elif [[ $output -eq 1 ]]; then
echo ON
else
echo Couldn''t understand plug response $output
fi
}
cmd_print_plug_status(){
query_plug "$payload_query"
}
cmd_print_plug_consumption(){
query_plug "$payload_emeter"
}
cmd_switch_on(){
send_to_plug $ip $port $payload_on > /dev/null
}
cmd_switch_off(){
send_to_plug $ip $port $payload_off > /dev/null
}
cmd_switch_toggle() {
output=`cmd_print_plug_relay_state`
if [[ $output == OFF ]]; then
cmd_switch_on
elif [[ $output == ON ]]; then
cmd_switch_off
else
echo $output
fi
}
##
# Main programme
##
check_dependencies
check_arguments
case "$cmd" in
on)
cmd_switch_on
;;
off)
cmd_switch_off
;;
toggle)
cmd_switch_toggle
;;
check)
cmd_print_plug_relay_state
;;
status)
cmd_print_plug_status
;;
emeter)
cmd_print_plug_consumption
;;
*)
show_usage
;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment