エアコンつけたいけどリモコンに手が届かない……という経験はないでしょうか。エアコンでなくとも似たような経験がある方は多いと思います。 そこで、より怠惰な生活を送るためにRaspberry Piを使ってスマートフォンからエアコンを操作できるシステムを作ることにしました。
これがシステムの概要図になります。拡張性のためにDiscordを介してRaspberry Piにメッセージを送信するようにしています。
エアコンは赤外線通信によって操作します。なのでライトやテレビのリモコンのような赤外線通信を行うリモコンであれば同じ要領でスマートリモコン化することができます。
1. ラズパイのセットアップ
Raspberry PiをWi-Fiに接続しSSHを利用可能にします。
既にわかりやすい記事を書いてくださっている方がいらっしゃるのでここは割愛します。
2. 赤外線送受信回路を作る
とてもわかりやすい記事を書いてくださっている方がいらっしゃるのでここも割愛します。
3. エアコンのリモコン信号を解析する
3.1 信号解析の必要性
記録した信号を送信すればエアコンを単純にON,OFFすることはできます。しかし、細かな操作をすることはできません。
というのも、エアコンのリモコンは、設定温度、運転モードなどすべてのデータを毎回エアコン本体に送信しているためです。例えば、夏場に冷房で運転しているときにエアコンをONにする信号を記録した場合、冬場に同じ信号でエアコンをONにすると冷房でエアコンが起動してしまいます。
これではあまりに使い勝手が悪いため、エアコンの信号を解析することでエアコンの状態の管理・信号の生成をRaspberry Pi側で行えるようにして実用に耐える使い勝手を実現しようというわけです。
3.2 リモコン信号のフォーマット
まずエアコンのリモコン信号がどのようになっているか簡単に解説します。
リモコン信号のフォーマットはこのページで詳しく解説されています。
日本製のエアコンのフォーマットは
- NECフォーマット
- 家製協(AEHA)フォーマット
- SONYフォーマット
の3種類に分類されます。以下の図はそれぞれのフォーマットでの信号の先頭部分です。
まずはエアコンがどのプロトコルを使用しているかを特定しましょう。
irrp.pyを使って信号を記録します。
python3 irrp.py -r -g18 -f codes --no-confirm aircon:on
それぞれのオプションの意味は以下の通りです。
オプション | 意味 |
---|---|
-r | 受信した信号を記録するモード |
-g18 | GPIO18を使って信号を受信する |
-f codes | 信号を記録するファイルを指定する(ここではcodesを指定) |
--no-confirm | 確認のために同じ信号を要求しない |
aircon:on | 信号の識別子 |
試しに、エアコンを起動する信号を記録してみます。 |
{"aircon:on": [3260, 1525, 437, 438, 361, 1168, 437, 438, 361, 438, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 438, 361, 1168, 437, 1168, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 1168, 437, 1168, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 1168, 437, 1168, 361, 438, 361, 438, 361, 438, 361, 1168, 361, 438, 361, 1168, 437, 438, 361, 438, 361, 1168, 437, 1168, 437, 1168, 437, 438, 361, 1168, 361, 1168, 437, 438, 361, 1168, 361, 1168, 437, 1168, 361, 1168, 437, 1168, 361, 438, 361, 438, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 438, 361, 1168, 437, 438, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 361, 438, 361, 1168, 437, 1168, 437, 438, 361, 1168, 437, 1168, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 438, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 1168, 437, 1168, 437, 438, 361, 1168, 437, 1168, 361, 1168, 437, 1168, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 361, 438, 361, 1168, 437, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 438, 361, 1168, 437, 1168, 437, 438, 361, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 438, 361, 438, 361, 438, 361, 1168, 437, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 361]}
このような信号が取れればOKです。この数列は赤外線が「ONの時間、OFFの時間、ONの時間……」という意味です。
図に書いてみるとこのようになっています。
もうひとつの上の図と照らし合わせてみると $1T \approx 400$程度とすれば$8T \approx 3200$,$4T \approx 1600$となるので、このエアコンは家製協(AEHA)フォーマットを使用しているようです。
3.3 信号を解析する
信号のフォーマットがわかったところで、いよいよ信号のどの部分で何を制御しているか解析していきます。
まず、このままでは長すぎて見にくいので以下のコードで信号を16進数に変換してみます。
import json
import os
import argparse
# オプション引数の定義
parser = argparse.ArgumentParser()
parser.add_argument("-f", "--file", help="Filename", required=True)
parser.add_argument("-s", "--save", help="saveFilename", type=str)
# オプション引数の取り出し
args = parser.parse_args()
# 指定されたファイルをjson形式で読み込み
with open(args.file, "r") as f:
rowRecodes = json.load(f)
# ファイルに保存されている全ての信号を16進数に変換する
for key in rowRecodes.keys():
interval = rowRecodes[key]
# 信号間隔を読み取って2進数に変換(AEHAフォーマット)
t = 0.0
binCode = []
for i in range(0, len(interval)-1, 2):
if t * 7.0 < interval[i]:
t = (interval[i] + interval[i+1]) / 12.0
binCode.append("")
elif interval[i+1] < t * 2.0:
binCode[-1] += "0"
elif interval[i+1] < t * 6.0:
binCode[-1] += "1"
# 16進数に変換
hexCode = ""
for c in binCode:
hexCode += format(int(c, 2), "x")
print(key + " : " + hexCode)
# ファイルがない場合 空のjsonファイルを作成
if not os.path.isfile(args.save):
with open(args.save, "w") as s:
s.write("{}")
# ファイルが空の場合 空のjsonファイルを作成
with open(args.save, "r") as s:
if len(s.read()) == 0:
s.write("{}")
# -s で指定されたファイルに書き込み
with open(args.save, "r") as s:
hexRecodes = json.load(s)
with open(args.save, "w") as s:
hexRecodes[key] = hexCode
s.write(json.dumps(hexRecodes))
python3 decode.py -f codes -s codes_hex
それぞれのオプションの意味は以下の通りです。
オプション | 意味 |
---|---|
-f codes | 信号が記録されたファイル(ここではcodesを指定) |
-s codes_hex | 変換した信号を記録するファイル(ここではcodes_hexを指定) |
以下のように変換されていればOKです。
{"aircon:on": "4a75c358a76f90af50ff00b748ef10fd02de21"}
しかしこれだけでは、どこで何を制御しているかわからないのでいくつか信号をとってみて差分を見ることにします。
試しに温度を変える信号をいくつかとってみましょう。
信号の規則はエアコンの機種によって全く異なると思うので、ここからはあくまで一例だと思って読んでいただければ幸いです。
{
"aircon:cool29": "4a75c358a76f90cf30ff00b748ef10fd02de21",
"aircon:cool28": "4a75c358a76f902fd0ff00b748ef10fd02de21",
"aircon:cool27": "4a75c358a76f90af50ff00b748ef10fd02de21",
"aircon:cool26": "4a75c358a76f906f90ff00b748ef10fd02de21",
"aircon:cool25": "4a75c358a76f90ef10ff00b748ef10fd02de21",
"aircon:cool18": "4a75c358a76f907f80ff00b748ef10fd02de21",
}
冷房で29度から25度まで1度ずつ変えて信号を記録してみました。最低温度である18度も記録しています。
{
"aircon:cool29": "4a75c358a76f90 < cf30 > ff00b748ef10fd02de21",
"aircon:cool28": "4a75c358a76f90 < 2fd0 > ff00b748ef10fd02de21",
"aircon:cool27": "4a75c358a76f90 < af50 > ff00b748ef10fd02de21",
"aircon:cool26": "4a75c358a76f90 < 6f90 > ff00b748ef10fd02de21",
"aircon:cool25": "4a75c358a76f90 < ef10 > ff00b748ef10fd02de21",
"aircon:cool18": "4a75c358a76f90 < 7f80 > ff00b748ef10fd02de21",
}
信号列をぐっと睨むと<>
で囲んだ部分が変わっていることがわかります。どうやらここが温度を制御している部分のようです。
これだけでは規則性がつかめないので、変わっている部分だけ抜き出して観察してみます。
温度 | 16進 | 2進 | 2進(前後逆転) | 16進(前後逆転) |
---|---|---|---|---|
29 | cf30 | 1100 1111 0011 0000 | 0000 1100 1111 0011 | 0cf3 |
28 | 2fd0 | 0010 1111 1101 0000 | 0000 1011 1111 0100 | 0bf4 |
27 | af50 | 1010 1111 0101 0000 | 0000 1010 1111 0101 | 0af5 |
26 | 6f90 | 0110 1111 1001 0000 | 0000 1001 1111 0110 | 09f6 |
25 | ef10 | 1110 1111 0001 0000 | 0000 1000 1111 0111 | 08f7 |
18 | 7f80 | 0111 1111 1000 0000 | 0000 0001 1111 1110 | 01fe |
ガチャガチャと操作してみると16進(前後逆転)では温度を$T$とすれば
0 | ~(32-T) | f | 32-T |
---|
という規則性があることがわかりました。(~
はbit反転の意)
運転モードについても同様に解析していきます。温度は20度に固定し運転モードを変えて信号をとってみました。
{
"aircon:on20": "4a75c358a7 < 6f90 > 3fc0ff00b748ef10fd02de21",
"aircon:off20": "4a75c358a7 < 7f80 > 3fc0ff00b748ef10fd02de21",
"aircon:auto20": "4a75c358a7 < ef10 > 3fc0ff00b748ef10fd02de21",
"aircon:cool20": "4a75c358a7 < 6f90 > 3fc0ff00b748ef10fd02de21",
"aircon:dry20": "4a75c358a7 < af50 > 3fc0ff00b748ef10fd02de21",
"aircon:clean20": "4a75c358a7 < 2fd0 > 3fc0ff00b748ef10fd02de21",
"aircon:warm20": "4a75c358a7 < cf30 > 3fc0ff00b748ef10fd02de21",
}
よく観察すると運転モードを制御する部分の信号は以下の表の温度と同じだということがわかります。
運転モード | 温度 |
---|---|
warm | 29 |
clean | 28 |
dry | 27 |
cool | 26 |
auto | 25 |
off | 18 |
onの信号はoffにする前の状態の信号を送信しているようです。
3.4 信号を生成する
これらを踏まえて所望の信号を生成するプログラムを書きます。
import os
import json
base = "4a75c358a76f907f80ff00b748ef10fd02de21"
modeNum = {"off": 18, "auto" : 25, "cool" : 26, "dry" : 27, "clean" : 28, "warm" : 29}
pulse = [[400, 400], [400, 1200]]
def conv_temp_to_hexcode(T):
"""
設定温度を16進制御コードに変換する
Parameters
----------
T : int
エアコンの設定温度
Returns
-------
(signal1, signal2) : tuple(str,str)
設定温度の16進制御コード
singal2 は singal1 の補ビット
"""
bincode = str(bin(32 - T)) # bincode = "0bxxxx"
# 4bitに整形
while len(bincode) < 6:
bincode = bincode[:2] + '0' + bincode[2:]
signal_1 = bincode[2:6] # bit部だけ取り出す
signal_1 = (signal_1)[::-1] # reverse
signal_2 = signal_1.translate(str.maketrans({'0': '1', '1':'0'})) # bit 反転
signal_1 = format(int(signal_1, 2), 'x')
signal_2 = format(int(signal_2, 2), 'x')
return signal_1, signal_2
def conv_mode_to_hexcode(mode):
"""
エアコンの運転モード(冷房,暖房 etc.)を16進制御コードに変換する
Parameters
----------
mode : string
エアコンの運転モード
Returns
-------
signal : str
運転モードの16進制御コード
"""
global modeNum
signal = conv_temp_to_hexcode(modeNum[mode])
return signal
def gen_code(mode, T):
"""
エアコンの16進制御コード全体を生成する
Parameters
----------
mode : string
エアコンの運転モード
T : int
エアコンの設定温度
Returns
-------
hexCode : str
エアコンの16進制御コード
"""
global base
# mode部を書き換え
tmp = conv_mode_to_hexcode(mode)
print("mode: " + tmp[0] + "f" + tmp[1] + "0")
base = base[:10] + tmp[0] + base[11:] # 10文字目をtmp[0]でreplace
base = base[:12] + tmp[1] + base[13:]
# 温度部を書き換え
tmp = conv_temp_to_hexcode(T)
print("temp: " + tmp[0] + "f" + tmp[1] + "0")
base = base[:14] + tmp[0] + base[15:]
base = base[:16] + tmp[1] + base[17:]
hexCode = base
print("hexCode: " + hexCode)
return hexCode
def encode(mode, T):
"""
エアコンの16進制御コードを赤外線LEDの点灯間隔に変換する
Parameters
----------
mode : string
エアコンの運転モード
T : int
エアコンの設定温度
Returns
-------
None
Note
-------
赤外線LEDの点灯間隔は同ディレクトリのairconに書き込まれる
"""
global pulse
hexcode = gen_code(mode, T)
signal = [3200, 1600]
# 2進変換
bincode = ""
for hx in hexcode:
code = str(bin(int(hx, 16)))
while len(code) < 6:
code = code[:2] + '0' + code[2:]
code = code[2:6]
bincode += code
# 2進 -> 点灯間隔
sig = [pulse[int(bit)][i] for bit in bincode for i in range(0,2)]
signal.extend(sig)
signal.append(400)
#print(signal)
# ファイルがない場合 空のjsonファイルを作成
if not os.path.isfile("aircon"):
with open("aircon", "w") as s:
s.write("{}")
# ファイルが空の場合 空のjsonファイルを作成
with open("aircon", "r") as s:
if len(s.read()) == 0:
s.write("{}")
# -s で指定されたファイルに書き込み
with open("aircon", "r") as s:
Recode = json.load(s)
with open("aircon", "w") as s:
Recode["aircon:op"] = signal
s.write(json.dumps(Recode))
if __name__ == '__main__':
mode = input()
T = int(input())
encode(mode, T)
書きました。
python3 encode.py
cool
27
mode: 6f90
temp: af50
hexCode: 4a75c358a76f90af50ff00b748ef10fd02de21
動かしてみてこのように出力されていればOKです。元の信号間隔の状態のコードが同ディレクトリのaircon
というファイルに出力されているはずです。
{"aircon:op": [3260, 1525, 437, 438, 361, 1168, 437, 438, 361, 438, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 438, 361, 1168, 437, 1168, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 1168, 437, 1168, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 1168, 437, 1168, 361, 438, 361, 438, 361, 438, 361, 1168, 361, 438, 361, 1168, 437, 438, 361, 438, 361, 1168, 437, 1168, 437, 1168, 437, 438, 361, 1168, 361, 1168, 437, 438, 361, 1168, 361, 1168, 437, 1168, 361, 1168, 437, 1168, 361, 438, 361, 438, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 438, 361, 1168, 437, 438, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 361, 438, 361, 1168, 437, 1168, 437, 438, 361, 1168, 437, 1168, 361, 1168, 437, 438, 361, 1168, 437, 438, 361, 438, 361, 1168, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 1168, 437, 1168, 437, 438, 361, 1168, 437, 1168, 361, 1168, 437, 1168, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 1168, 361, 438, 361, 1168, 437, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 437, 438, 361, 1168, 437, 1168, 437, 438, 361, 1168, 437, 1168, 437, 1168, 437, 1168, 437, 438, 361, 438, 361, 438, 361, 1168, 437, 438, 361, 438, 361, 438, 361, 438, 361, 1168, 361]}
あとはお好みで風向きや風量を制御する部分を解析して同じようにエンコードしてみてください。
コードはすべてこちらのリポジトリにあげてあります。
参考
4. Discord Botを作る
4.1 Botアカウントの作成
メッセージを監視するためのDiscord Botを作成します。
まずはDiscordのサーバーを作成します。
Discordで サーバーの追加 > オリジナルの作成 > 自分と友達のため > 新規作成
で新しいサーバーを作成します。名前はなんでもいいです。
次にDiscord Developers Portalへアクセスし、BOTのアカウントを作成します。Developers PortalからログインするとBrowser版のホームに飛ばされますが、再度アクセスすればDeveloper Portalへアクセスできます。
Portalへアクセスできたら Applications > New Application > Create
で新しいApplicationを作成します。
ここではApplicationの名前をremocon_testとしました。Bot > Add Bot > Yes, do it!
でApplicationをBotとして登録します。
(testのような多く使われている名前だとBotの名前として使用できないことがあるようです)
4.2 Botアカウントの設定
Bot登録が完了すると、Botの設定画面が現れるはずです。PUBLIC BOTはOFFにしておきましょう。
TOKENをコピーしてBotを動かしてみます。
OAuth2のOAuth2 URL GeneratorでBotの権限を設定します。
SCOPESはbot、BOT PERMISSIONSはView Channels, SendMessagesを選択します。これはメッセージの読み取りと、送信の権限です。
生成されたURLをコピーし、アドレスバーにペーストします。
するとこのようなページに飛ぶのでBotを追加したサーバーを選択してはいを押します。
権限はこの2つが設定されていればOKです。認証を押して次に進みます。
Botの認証が完了すれば、選択したサーバーにBotが参加しているはずです。これでBotの設定は完了です。
参考
4.3 Botのテスト
Botのプログラムを組んでテストしてみます。
import discord
TOKEN = "Paste Your TOKEN"
client = discord.Client()
@client.event
async def on_message(message):
if message.content == "ON":
await message.channel.send("message is ON")
elif message.content == "OFF":
await message.channel.send("message is OFF")
client.run(TOKEN)
TOKENは自分のBotのTOKENに置き換えてください。TOKENはBotのページからコピーできます。
discord
はプリインストールされていないので
pip3 install discord
でインストールしてください。
python3 discord_test.py
でプログラムを実行すればBotがオンラインになるはずです。
試しにON
と送信してみましょう。
Botからメッセージが返ってきました!これでBotのテストは完了です。
4.4 メッセージに対応した信号を送信する
subprocess
をつかってirrp.py
を呼び出し、信号を送信します。
import discord
import subprocess
import encode
TOKEN = "Paste Your TOKEN"
client = discord.Client()
@client.event
async def on_message(message):
if message.content == "ON":
encode.encode('cool', 27)
subprocess.run(["python3", "./irrp.py", "-p", "-g17", "-f", "./aircon", "aircon:op"])
await message.channel.send("message is ON")
elif message.content == "OFF":
encode.encode('off', 27)
subprocess.run(["python3", "./irrp.py", "-p", "-g17", "-f", "./aircon", "aircon:op"])
await message.channel.send("message is OFF")
client.run(TOKEN)
これでエアコンのON,OFFができればOKです。
5. エアコンの状態をラズパイ側で管理する
ここまででエアコンの単純なON,OFFはできるようになりました。ここからはエアコンの状態を管理できるようにします。
SQLiteをインストールしDBを作成します。
sudo apt install sqlite
sqlite3 db.sqlite3
テーブルを作成し、初期データを入れておきます。
sqlite> create table user_data(aircon_mode, aircon_temp);
sqlite> insert into user_data values('cool', 27);
sqlite> select * from user_data;
off|27
sqlite> .exit
サーバ側からSQLiteにアクセスしてデータをやりとりできるようにします。
import discord
import subprocess
import sqlite3
import encode
connect = sqlite3.connect('db.sqlite3')
cursor = connect.cursor()
TOKEN = "Paste Your TOKEN"
client = discord.Client()
@client.event
async def on_message(message):
if message.content == "ON":
cursor.execute("SELECT aircon_mode, aircon_temp FROM user_data")
state = cursor.fetchall()[0]
mode = state[0]
temp = state[1]
encode.encode(mode, temp)
cursor.execute("UPDATE user_data SET aircon_mode = ?", (mode,))
cursor.execute("UPDATE user_data SET aircon_temp = ?", (temp,))
subprocess.run(["python3", "./irrp.py", "-p", "-g17", "-f", "./aircon", "aircon:op"])
await message.channel.send("message is ON")
elif message.content == "OFF":
cursor.execute("SELECT aircon_temp FROM user_data")
state = cursor.fetchall()[0]
temp = state[0]
encode.encode('off', temp)
cursor.execute("UPDATE user_data SET aircon_mode = ?", ('off',))
subprocess.run(["python3", "./irrp.py", "-p", "-g17", "-f", "./aircon", "aircon:op"])
await message.channel.send("message is OFF")
client.run(TOKEN)
これでラズパイ側でエアコンの状態を管理して利用できるようになりました。
6. メッセージに対応した信号を送信する
正規表現を使ってひらすらパターンを列挙していきます。正規表現についてはこちらを参照してください。
データベースの更新、エアコンの状態の取得、irrp.py
の呼び出しは関数にしてまとめました。
Discord BotのTOKENもデータベースで管理するようにしました。
ついでにライトの制御もできるようになっています
import discord
import re
import subprocess
import sqlite3
import encode
connect = sqlite3.connect('db.sqlite3')
cursor = connect.cursor()
cursor.execute("SELECT token from token")
TOKEN=str(cursor.fetchall()[0][0])
client = discord.Client()
def dbUpdate(column, state):
"""
データベースの内容を更新する
Parameters
----------
column : str
変更するカラム
state : int or str
セットする値
"""
if column == 'light':
cursor.execute("UPDATE user_data SET light = ?", (state,))
elif column == 'aircon_temp':
cursor.execute("UPDATE user_data SET aircon_temp = ?", (state,))
elif column == 'aircon_mode':
cursor.execute("UPDATE user_data SET aircon_mode = ?", (state,))
connect.commit()
def getState():
"""
エアコンの運転モードと設定温度を取得する
Returns
----------
state : list[str, int]
エアコンの運転モードと設定温度
"""
cursor.execute("SELECT aircon_mode, aircon_temp FROM user_data")
return list(cursor.fetchall()[0])
def lightON():
proc = subprocess.run(["python3", "./irrp.py", "-p", "-g17", "-f", "./codes", "light:on"])
def lightOFF():
proc = subprocess.run(["python3", "./irrp.py", "-p", "-g17", "-f", "./codes", "light:off"])
def airconOP(state):
encode.encode(state[0], state[1])
proc = subprocess.run(["python3", "./irrp.py", "-p", "-g17", "-f", "./aircon", "aircon:op"])
@client.event
async def on_message(message):
message.content = str(message.content).replace(' ', '')
if not re.match(r'\$', message.content) is None:
pass
elif not re.search(r'電気\S*つけて', message.content) is None:
lightON()
dbUpdate('light', 1)
elif not re.search(r'電気\S*消して', message.content) is None:
lightOFF()
dbUpdate('light', 0)
elif not re.search(r'エアコン\S*つけて', message.content) is None:
airconOP(['off', 27])
state = getState()
airconOP(state)
dbUpdate('aircon_mode', state[0])
elif not re.search(r'エアコン\S*消して', message.content) is None:
temp = getState()[1]
airconOP(['off', temp])
dbUpdate('aircon_mode', 'off')
elif not re.search(r'エアコン\S*冷房', message.content) is None:
airconOP(['cool', 27])
dbUpdate('aircon_mode', 'cool')
dbUpdate('aircon_temp', 27)
elif not re.search(r'エアコン\S*暖房', message.content) is None:
airconOP(['warm', 20])
dbUpdate('aircon_mode', 'warm')
dbUpdate('aircon_temp', 20)
elif not re.search(r'温度\S*下げて', message.content) is None:
state = getState()
state[1] -= 1
airconOP(state)
dbUpdate('aircon_temp', state[1])
elif not re.search(r'温度\S*上げて', message.content) is None:
state = getState()
state[1] += 1
airconOP(state)
dbUpdate('aircon_temp', state[1])
elif not re.match(r'now', message.content) is None:
cursor.execute("SELECT * FROM user_data")
await message.channel.send(str(cursor.fetchall()))
client.run(TOKEN)
コードはすべてこちらのリポジトリにあげてあります。
おまけ. Google Assistantと連携してボイスコントロールする
IFTTTを利用してGoogle AssistantとDiscordを連携させます。
まず、IFTTTにアクセスして、メールアドレスを登録します。
Create
でアプリの作成画面に移ります。
Add
を押してトリガーとなるアプリを選択します。Google Assistantを選択しましょう。
ここではSay a phrase with a text ingredient
を選択します。
Google Assistantを利用しているGoogleアカウントと連携します。
呼び出すためのキーフレーズを設定します。ここではリモコン
に設定しました。$
はテキスト変数です。
つまりリモコン エアコンつけて
とGoogle Assistantに言うとリモコンつけて
の部分がDiscordに送信されます。
Create triggerを押してトリガーを作成します。
Then That
のAddを押して連携するサービスを選択します。ここではWebhooksを選択しましょう。
action fieldは以下のように設定してください。URLの部分は後述するDiscordのwebhookURLです。
DiscordのWebhookURLを生成します。サーバー設定 > 連携サービス > ウェブフックの作成
で新しくWebhookURLを作成できます。
名前は何でもいいです。ウェブフックURLをコピー
を押すとURLがクリップボードにコピーされるので、先程のURL欄にペーストしてください。
Createを押したらGoogle Assistantとの連携は完了です。
レスポンスがとても遅い(5分〜)ことがあります。テストしたい場合はAdvanced REST clientなどを利用してリクエストを送ってみてください