Motivation
子供が無制限にTVを見てしまうので、見たいTV番組以外は自動でTVへの給電ON/OFFを行うため、tp-linkのHS105というのを買ってみた。iPhoneからスケジュール設定できる。
https://www.tp-link.com/jp/home-networking/smart-plug/hs105/
スケジュールした時間に給電OFFになっても横のボタンで物理的にONできるので、ボタン押したらTV点くことを子供が覚えてしまった。厚紙で蓋をしたけど、無理やり押す。。。
埃汚くてすいません。。。
やったこと
HS105にはAPIがあって、その制御スクリプトが公開されているので、HS105のボタンを押そうがcronで30秒以内に給電OFFにするよう改変した(以下diff参照)。。。
(1年前にやったので下記版を改変)
https://github.com/softScheck/tplink-smartplug/blob/a094843c64bbfe62780f0e74412c090ad15a9f6d/tplink_smartplug.py
1c1
< #!/usr/bin/env python2
---
> #!/usr/bin/python
25a26,29
> import json
> import datetime
> import time
>
72,86d75
< # Parse commandline arguments
< parser = argparse.ArgumentParser(description="TP-Link Wi-Fi Smart Plug Client v" + str(version))
< parser.add_argument("-t", "--target", metavar="<hostname>", required=True, help="Target hostname or IP address", type=validHostname)
< group = parser.add_mutually_exclusive_group(required=True)
< group.add_argument("-c", "--command", metavar="<command>", help="Preset command to send. Choices are: "+", ".join(commands), choices=commands)
< group.add_argument("-j", "--json", metavar="<JSON string>", help="Full JSON string of command to send")
< parser.add_argument('--influxdb', type=str, metavar=("URI", "database"), default=None,
< nargs=2, help='If command is "energy", push to influxdb. URI should point to influxdb, e.g. [http/https]://<ip>:<port>. Database: e.g. smarthome.')
< parser.add_argument('--influxdb_energy', type=str, metavar="query", default=None,
< help='query to store energy as Joule, e.g. energy,type=elec,device=hs110-1 this will be appended with <energy in joule> (as int)')
< parser.add_argument('--influxdb_power', type=str, metavar="query", default=None,
< help='query to store power as Watt, e.g. power,type=elec,device=hs110-1 this will be appended with <power in W> (as float)')
< args = parser.parse_args()
<
<
88c77
< ip = args.target
---
> ip = '192.168.0.7'
90,95c79,80
< if args.command is None:
< cmd = args.json
< else:
< cmd = commands[args.command]
<
<
---
> cmd = '{"system":{"get_sysinfo":{}}}'
> cmd2 = '{"system":{"set_relay_state":{"state":0}}}'
97a83
> while True:
98a85,88
> dt_now = datetime.datetime.now()
> print dt_now
> if dt_now.hour >= 21:
> break
105,112c95,105
< print "Sent: ", cmd
< print "Received: ", decrypt(data[4:])
< except socket.error:
< quit("Cound not connect to host " + ip + ":" + str(port))
<
< if (args.command == "energy") and (args.influxdb != None):
< import requests
< import json
---
> json_dict = json.loads(decrypt(data[4:]))
> relay_state = json_dict['system']['get_sysinfo']['relay_state']
> print "relay_state: ", relay_state
> if relay_state == 1:
> sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
> sock_tcp.connect((ip, port))
> sock_tcp.send(encrypt(cmd2))
> data = sock_tcp.recv(2048)
> sock_tcp.close()
> time.sleep(30)
114,129c107
< # Get total_wh and power_mw from json response
< energy_response = decrypt(data[4:])
< energy_wh = json.loads(energy_response)['emeter']['get_realtime']['total_wh']
< energy_joule = int(energy_wh)*3600
< power_mW = json.loads(energy_response)['emeter']['get_realtime']['power_mw']
< power_W = float(power_mW)/1000.0
<
< # Build URI and query
< # Something like req_url = "http://localhost:8086/write?db=smarthometest&precision=s"
< # Something like post_data = "water,type=usage,device=devicename value=1"
< req_url = args.influxdb[0]+"/write?db="+args.influxdb[1]+"&precision=s"
< post_data = ""
< if (args.influxdb_energy != None):
< post_data = args.influxdb_energy+" value="+str(energy_joule)
< if (args.influxdb_power != None):
< post_data += "\n"+args.influxdb_power+" value="+str(power_W)
---
> #00 19 * * 1 /home/pi/my_tplink_smartplug.py >> /var/log/tplink.log 2>&1
131,137c109,110
< # Post data to influxdb, check for obvious errors
< try:
< httpresponse = requests.post(req_url, data=post_data, verify=False, timeout=5)
< if (httpresponse.status_code != 204):
< print "Push to influxdb failed: " + str(httpresponse.status_code) + " - " + str(httpresponse.text)
< except requests.exceptions.Timeout as e:
< print "Update failed due to timeout. Is influxdb running?"
---
> except socket.error:
> quit("Cound not connect to host " + ip + ":" + str(port))
95-105行目あたりで cmd = '{"system":{"get_sysinfo":{}}}'
で給電ON/OFFステータスrelay_state
を取得して、ON: 1
にされてたら cmd2 = '{"system":{"set_relay_state":{"state":0}}}'
でOFFにするだけ。
87行目あたりで21時まで有無を言わさず無限ループ
cron設定
常時動いているraspberry piにTV番組終了に合わせてcronでスクリプト起動。さて何のTV見てるでしょう?
32 18 * * 1 /home/pi/my_tplink_smartplug.py > /var/log/tplink.log 2>&1
17 19 * * 2 /home/pi/my_tplink_smartplug.py > /var/log/tplink.log 2>&1
32 18 * * 3 /home/pi/my_tplink_smartplug.py > /var/log/tplink.log 2>&1
17 19 * * 4 /home/pi/my_tplink_smartplug.py > /var/log/tplink.log 2>&1
57 19 * * 5 /home/pi/my_tplink_smartplug.py > /var/log/tplink.log 2>&1
仕事中にTVが消えたとメールで連絡が来るが、外からは家に入れないので、家に帰らないと無理と言って、諦めてもらう。。。
余談
本題とは関係ありませんが、下記を参照してDynamic DNSを使って外からSSHで家に入れるようにした。
送信元アドレス制限しないと、22番ポートにメッチャ不正アクセス来ます。
AWSのRoute 53でドメインホストしているので、下記のようなスクリプトをclonで定期的に動かして、内部サーバにポートフォワードかけてるHGWのグローバルアドレスのAレコードを更新
ip.txt
に前回のアドレスを書いておいて、変わってたらaws-cliで更新
# !/bin/sh
NEWIP=`/usr/bin/curl -s https://api.ipify.org`
IP=`cat /home/pi/ip.txt`
if [ -n $NEWIP ] && [ $IP != $NEWIP ]; then
cp /home/pi/template.json /home/pi/current.json
sed -i 's/<IP>/'"$NEWIP"'/' /home/pi/current.json
echo $NEWIP > /home/pi/ip.txt
/usr/bin/aws --debug --output=table route53 change-resource-record-sets --hosted-zone-id ドメインのzone id(ドメイン名ではない) --change-batch file:///home/pi/current.json
fi
更新のDNSレコード更新のtemplateはこんな感じ
{
"Comment": "Update by updatedns.sh",
"Changes": [
{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "ホスト名",
"Type": "A",
"TTL": 300,
"ResourceRecords": [
{
"Value": "<IP>"
}
]
}
}
]
}