Manejo de enchufes inteligentes TP-Link HS100 HS110 con OpenWrt
Desde hace tiempo vengo ejecutando con openwrt scripts para el manejo de enchufes inteligentes. Los más asequibles para el manejo desde ash han sido los Belkin Wemo. Desde hace algún tiempo vienen escaseando, y los que se encuentran, están a precio desorbitado. Por esa razón me he decidido a buscar una alternativa barata a los Belkin Wemo.
Los enchufes TP-Link HS100 y HS110 parecen buenos candidatos. Son baratos y por ahí circula un script (Author George Georgovassilis,
https://github.com/ggeorgovassilis/linuxscripts) para manejarlos desde bash. La única pega es que el script directamente no es compatible con ash bajo openwrt.
Después de tener cierto tiempo y estudiar el script, he realizado una adaptación del script que corre bajo ash, con lo que podemos manejar el enchufe inteligente desde la línea de comandos de openwrt. En mi caso es útil para encender todo tipo de aparatos conectados a los enchufes una vez que se ha ido la luz, y que cuando vuelve se quedan todos apagados. Al volver la luz, sólo necesito acceso al router con openwrt, ya que los enchufes estarán preparados y conectados a la wifi del router. Accediendo empiezo a encender la secuencia de enchufes y todos los aparatos conectados a él. Lo utilizo principalmente en una casa con una persona que no puede valerse por sí misma.
Manejar los enchufes bajo luci lo podemos realizar incorporando el módulo
luci-app-commands. Con este módulo podemos definir comandos a ejecutar con un botón en la interfaz gráfica de luci. Es una forma primitiva de automatización, pero muy efectiva, y recordemos que sólo necesitamos el router openwrt. Nada de servidores de automatización (aunque a esos evidentemente también se puede incorporar).
Para hacerlo compatible necesitamos instalar también una serie de módulos para usar comandos completos, que en busybox no están al completo:
coreutils-base64
coreutils-od
coreutils-printf
Ahora subimos el siguiente código al router. Yo lo suelo poner en /etc para que al hacer un backup de la configuración, el script también vaya incluído. En este caso he escogido /etc/hs100_control.sh
#!/bin/sh
##
# 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
#
# 2020-06-22 : Tki2000 modifications for running on openwrt ash command shell via a tmp file
# Needed modules: coreutils-base64 coreutils-od coreutils-printf
# Working on: Hw:4.0 Fw: 1.0.2
_tmpfile=/tmp/tplink.txt
#echo args are $@
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 program 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 program 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 program 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 program 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 program 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 $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" " "`
args_for_printf=""
for element in $input_num
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" > $_tmpfile
cat $_tmpfile | decode
echo
}
# plug commands
cmd_print_plug_relay_state(){
send_to_plug $ip $port "$payload_query" | decode > $_tmpfile
output=`cat $_tmpfile | egrep -o 'relay_state":[0,1]' | egrep -o '[0,1]'`
#echo $output
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_print_plug_relay_state
}
cmd_switch_off(){
send_to_plug $ip $port $payload_off > /dev/null
cmd_print_plug_relay_state
}
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 program
##
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
Le damos permisos de ejecución con chmod +x /etc/hs100_control.sh
Y ahora podemos apagar/encender/manejar el enchufe tal que así:
/etc/hs100_control.sh 192.168.x.x 9999 on
/etc/hs100_control.sh 192.168.x.x 9999 off
/etc/hs100_control.sh 192.168.x.x 9999 check
/etc/hs100_control.sh 192.168.x.x 9999 toggle
/etc/hs100_control.sh 192.168.x.x 9999 emeter
Cambiad la IP 192.168.x.x por la que tenga el enchufe asignada por DHCP. Para distinguir varios enchufes podemos cambiar la IP por el nombre del host, y asignar la MAC del enchufe a un nombre de host distinto en las opciones DHCP de luci.
Podemos meter cada uno de los comandos anteriores en los comandos propios de luci que nos sale al instalar el módulo
luci-app-commands.
El puerto siempre parece ser 9999, así que si alguien lo quiere incorporar al script directamente sin ser argumento, es libre de hacerlo.
Por ahora lo que he probado con la última versión de hardware 4.0 y firmware 1.0.2 ha funcionado perfecto. ==>> Actualizado a 1.1.1 sigue funcionando perfecto.
No sé qué manía tienen en hacer que estos aparatejos sólo funcionen con aplicaciones de móvil o chorradas por el estilo. Cuanto más genéricos sean, más aplicaciones podrán tener.
La única pega, al igual que los wemo, es que para configurarlos hay que hacerlo con la aplicación del móvil. Una vez que estén configurados, y yo lo hago para trabajar exclusivamente en local (nada de nubes ni chorradas de esas) son completamente autónomos en la conexión.
Con el tiempo veré si son igual de estables que los wemo, y son capaces de aguantar las conexiones de ON durante meses, sin microcortes.
He medido el cosumo de un HS100 en estado de reposo y me ha dado unos 0.3W. Un Belkin Wemo me da unos 1.3W. La ganancia en consumo es sustancial.
Por ahora van bien.
Usad estos conocimientos bajo vuestra propia responsabilidad.