リビング照明にはリモコンがついていて、Nature Remoでつけたり消したりしているんですが(実際はアレクサがやってくれている)、寝るときは壁スイッチで消すことが多いです。そっちのほうが動線的に自然だし。
ただ、これだと主電源が切られたようなもんで、もう一回その壁スイッチを押して主電源をOnにしないとリビング照明がつきません。「アレクサ、電気つけて」といってNature Remoがピッて言っても、電気がつきません。
ということで、壁スイッチの横にAWSボタンを置いて、壁スイッチを押さずにAWSボタンを押すことで電気を消すようにしました。以下の図の、右下のボタンです。(左上のSwitchbotのボタンは今回は関係ないです)
ちなみに、これ、正式名称は「AWS IoT Enterprise Button」というみたいですが、2022/5月時点で販売中止になってました。安くてよかったのに。。代替品はめちゃ高いので買う気がしません。。。
https://aws.amazon.com/jp/iot-1-click/devices/
なので、ボタンはこの1個だけで、今後の増設はNFCタグで行います。詳しくは以下で!
仕組み = ボタン → Lambda → Nature Remo → 電気消す
ボタンを押したら、Lambdaが動いてNatureRemoに指示を出し、NatureRemoがその指示に従って電気を消す という流れです。
以前はIFTTTを使っていましたが、IFTTTが有料になったため、IFTTTを使わずにLambdaだけで行います。
Lambdaとは、AWSが提供しているサービスの一つで、自分でPythonなどのプログラムを書いて、SwitchbotやNature Remoなどを動かすことができます。ちなみに、AWSのサービスは有料で、クレカの登録が必要になります。が、今回のような「単にボタンを押したらRemoを動かす」だけだったら、月に100円程度です♪
事前準備
AWS IoTボタンのセットアップ
スマホを使って登録します。AWS IoT 1-Clickというアプリをスマホにインストールしてください。登録までの流れはアプリがわかりやすくリードしてくれます。
ちなみに登録にはDSN番号なるものが必要です。ボタンの背面に記載されているので、ボタンを壁に貼り付ける前にメモっておく必要があります!
(箱にも書かれているので、箱があれば大丈夫ですが)
AWSアカウントの作成
まずはAWSアカウントを作りましょう。利用料金がかかるしクレジットカードの登録も必要ですが、臆さず登録しましょう!
https://aws.amazon.com/jp/register-flow/
すさまじくいろいろなサービスがありますが、このなかの「Lambda(ラムダ)」を使います。
Nature RemoのTOKENを取得
以下のサイトへ行って、TOKENを発行しましょう。画面に一度そのTOKENが出てくるだけで、あとから再表示はできないようなので、しっかりTOKENをメモっておきましょう。
https://home.nature.global/
僕は以下のサイトを参考にしました。
https://blog-and-destroy.com/12297
これで準備は完了です!次はいよいよLambdaでプログラムを書いて、自分のNature Remoとやり取りします。
Nature Remoに登録されている家電IDを調べる
Lambda関数の新規作成(空)
AWSで作業をします。AWSコンソールに行ったら、まずはリージョンのチェック!画面の右上でリージョンを変更できます。「東京」になっていればOK。「バージニア北部」とかになっていれば、「東京」に変更してください。これ、すごく重要です!
次に、画面上部にサービスの検索窓があるので、そこに「Lambda」と入れて、出てきたサービス欄からLambdaをクリック。
右上の「関数の作成」をクリック。
関数名 = 「NatureRemo_GetDevieID」
ランタイム = 「Python3.9」(2022/5時点。その時の最新のを選べばOKです)
と入れて、あとはデフォルト設定のままで、右下の「関数の作成」ボタンをクリック。
以下のブログが参考になりました。
https://zenn.dev/nakam_aws/articles/46fcc003855aef
環境変数の設定(TOKEN)
環境変数へ、TOKEN情報を書き込みます。(プログラム内に直書きでもOKですが、環境変数に書いたほうがすっきりします)
「設定」タブ→「環境変数」→「編集」と押し、
「環境変数の追加」で、キー欄に「TOKEN」と、値欄に先ほどメモしたRemoのTOKEN番号を貼り付けましょう。
キー 「TOKEN」
値 RemoのTOKEN番号
入力したら、保存してください。
Remoの家電ID(ApplianceID)を取得するプログラムを書く①
まずは、以下のコードを貼り付けてください。ちなみに、はまったところは、「認証が通らない」「Requestsライブラリが使えない」といった点でした。
import json
import os
import urllib.request
def lambda_handler(event, context):
#--- TOKEN ---------------#
TOKEN = os.environ.get('TOKEN')
#--- ヘッダ --------------#
headers = {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + TOKEN,
}
#-- 家電ID取得用のURL --#
url = 'https://api.nature.global/1/appliances'
#--- 取得
req = urllib.request.Request(url=url,headers=headers, method='GET')
#--- 取得 ----------------#
res = urllib.request.urlopen(req)
jjj = json.loads(res.read())
#--- ログに出力(全部)---#
print('-----------------')
print(json.dumps( jjj, indent=4, ensure_ascii=False ))
print('-----------------')
このコードを貼り付けたら、上のほうにある「Deploy」ボタンを押してください。これでプログラムが実行できる状態になりました。
Deployボタンを押した後、テストタブを開いて、
テストタブ
右側にあるオレンジの「テスト」ボタンを押してください。
以下のような画面がでて、テストが成功したら、もう一回「コード」タブに戻ってください。
成功したら、このような緑の枠組みが出てきます。
「Execution Result」が表示されていると思います。そこにログが出力されています。
ただ、ログがぐちゃっとなって見づらいと思います。
Remoの家電ID(ApplianceID)を取得するプログラムを書く②
ID/名前/ボタン情報 だけを出力するようにします。上記のコードの最後に以下のプログラムを追加してください。
#--- ログに出力(IDだけ)---#
for j in jjj:
print('-----------------')
print('id=' + j['id'])
print('name=' + j['nickname'])
print('signal=' + json.dumps(j['signals'], ensure_ascii=False))
Deployを忘れずに!Deployしたら、テストタブに行って、テスト実行して、コードに戻って、ログを見てみてください。
以下のようなのがログに出てきていると思います。idが「家電ID」で、ついでに後で必要になるシグナルIDも取得できました。
Remoに登録されているリビング照明の家電IDは「xxxxx」で、照明をつけるオンボタンのシグナルIDは「yyyyy」、オフボタンのシグナルIDは「zzzzz」という感じです。
-----------------
id=xxxxx
name=電気
signal=[{"id": "yyyyy", "name": "オン", "image": "ico_on"}, {"id": "zzzzz", "name": "オフ", "image": "ico_off"}]
-----------------
ちなみに、シグナルIDに何も出ていないことがあります。これは、リモコンのボタン1つひとつを登録するのではなく、リモコンをマルっと登録したものになります。TVとかエアコンとかに多く起こります。この場合、ちょっとやり方が変わります(後述)。
※ シグナルに何も出てこないパターン
-----------------
id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
name=テレビ
signal=[]
-----------------
LambdaでRemoを動かす!
シグナルIDがあるケース
うちのRemoに登録されている家電「電気」は、シグナルIDがあったので、このケースです。
新たにLambda関数を新規作成しましょう。名前は、「NatureRemo_LightOn」とか。
そして、環境変数にTOKEN情報を書き込んでください。
そしたら、デフォルトで入っているコードを消して、以下のコードを貼り付けてください。
また、「yyyyy」の部分(url_lightのところ)を、上記で取得したシグナルIDに書き換えてください。
import json
import os
import urllib.request
def lambda_handler(event, context):
#--- TOKEN ---------------#
TOKEN = os.environ.get('TOKEN')
#--- ヘッダ --------------#
headers = {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + TOKEN,
}
#--- 電気用のURL ------#
url_light = 'https://api.nature.global/1/signals/yyyyy/send' #←要修正!yyyyy
#--- 電気を消す -------------------------#
req = urllib.request.Request(url=url_light, headers=headers, method='POST')
urllib.request.urlopen(req)
そうです。家電IDは使いません♪ シグナルIDだけで動きます!
Deployし、テストタブに行き、テストを実行してください。これで電気がつけば、成功です!
僕は、初めて成功したときは結構感動しました^^
TVやエアコンなど、リモコンをマルっと登録した家電にはシグナルIDがつきません。そんな時は、以下のような感じで動かします。
関数を新規作成し、環境変数にTOKEN情報を入れ、コード欄に以下のコードを貼り付けてください。
こちらのケースでは、家電IDを使うことになります。
urlのところにあるxxxxx・・・を、家電IDに書き換えてください。
import json
import os
import urllib.request
def lambda_handler(event, context):
TOKEN = os.environ.get('TOKEN')
url = 'https://api.nature.global/1/appliances/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/tv'
headers = {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + TOKEN,
}
data = b'button=power'
req = urllib.request.Request(url=url, data=data, headers=headers, method='POST')
urllib.request.urlopen(req)
ポイントは、「data = b’button=power’」のところ。”power”が、電源ボタンを指します。すなわち、これを動かすと、TVリモコンの電源ボタンを押した動きになります。
このように、どのボタンを押すかをデータで渡す必要があるのですが、その渡し方にはまった。。。
以下のブログに解決策が書かれており、非常に助かりました。
https://qiita.com/souring001/items/68b5397c1750c325f61a
TVボタン名の調べ方
ボタン名(name)を調べるには、以下のコードを新規作成して回してみてください(TOKEN情報を環境変数に入れるのを忘れずに)。
import json
import os
import urllib.request
def lambda_handler(event, context):
#--- TOKEN ---------------#
TOKEN = os.environ.get('TOKEN')
#--- ヘッダ --------------#
headers = {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + TOKEN,
}
#-- DeviceID取得用のURL --#
url = 'https://api.nature.global/1/appliances'
req = urllib.request.Request(url=url,headers=headers, method='GET')
#--- 取得 ----------------#
res = urllib.request.urlopen(req)
jjj = json.loads(res.read())
#--- ログに出力(IDだけ)---#
for j in jjj:
print('-----------------')
print('id=' + j['id'])
print('name=' + j['nickname'])
print('signal=' + json.dumps(j['signals'], ensure_ascii=False))
if('tv' in j.keys()):
print('TV=' + json.dumps(j['tv']['buttons'], ensure_ascii=False))
if('aircon' in j.keys()):
print('aircon=' + json.dumps(j['aircon'], ensure_ascii=False))
ログに、以下のような情報が出ているはずです。ここから、どのボタンがどんな名前か勘を働かせて特定します。
TVのボタン名一覧
これで、「Lambda→Remo」の部分が組みあがりました!一番面倒なLambdaはこれでおしまいです。
あともう少しです!「ボタン→Lambda」の部分を組み込めば完成です!!
ボタンを押したらLambdaを動かす
こちらは、ボタンを登録したときに使ったAWS IoT 1-Clickスマホアプリを使います。
「プロジェクト」タブから新規作成し、「デバイステンプレート」で、先ほど作ったLambda関数(NatureRemo_LightOn)を選択し、「プレイスメント」で冒頭に登録したIoTボタンを指定してあげるといった流れです。
僕はスマホアプリでトラブルなく作成できましたが、他の人はWebのAWSサービス(IoT 1-Click)で設定されてる人が多いみたいです。
https://qiita.com/hayao_k/items/d36d4b2579d0bbbe21a9
などがとても分かりやすかったです。
これで完成!ボタンをぽちっとして、ちゃんと電気が消えるか試してみましょう♪
おまけ:実際に使っているコード
実際には、電気を消すと同時にTVも消したいですよね。また、このAWS IoTボタンは、ダブルクリックなどにも対応してます。なので、ダブルクリックしたときは、電気をつけるようにしています。
テレビのボタンって、「On/Offボタン」が独立してそれぞれあるわけじゃなく、「電源ボタン」がひとつしかない。。でも、「電源がついてなければ電源ボタン押すのスルーして」なんてことはできない。なので、ちょっとした工夫が必要です。
いったん「入力切替」ボタンを押しています。もしテレビが消えていたら、これでいったんテレビがOnになります。もしテレビがついていたら、ついたままです。その後、電源ボタンを押しています。電源ボタンを押す前に「必ずついてる状態」にしている感じです。
加えて、NFCタグからも実行させているので、その部分も書き加えています。
なお、環境設定に、TOKEN / 照明OnのボタンシグナルID / 照明OffボタンのシグナルIDの3つを登録しています。
import json
import os
import urllib.request
import time
def lambda_handler(event, context):
#--- ID情報を環境設定から取得 ---#
ID_TV = os.environ.get('ID_TV')
ID_LIGHTOFF = os.environ.get('ID_LIGHTOFF')
ID_LIGHTON = os.environ.get('ID_LIGHTON')
#--- TOKEN ---------------#
TOKEN = os.environ.get('TOKEN')
#--- ヘッダ --------------#
headers = {
'accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + TOKEN,
}
#--- AWSボタンとNFCタグの両方からくる-> NFCタグから来たら、シングルクリックにする
if(json.dumps(event).find('deviceEvent') == -1):
clicktype = 'SINGLE'
else:
clicktype = event['deviceEvent']['buttonClicked']['clickType']
#----------------------------------------------------------------#
#--- ダブルクリックなら電気をつけて、それ以外は電気を消す -------#
#----------------------------------------------------------------#
#--- 電気用のURL ------#
if (clicktype == "SINGLE"):
url_light = 'https://api.nature.global/1/signals/' + ID_LIGHTOFF + '/send'
elif (clicktype == "DOUBLE"):
url_light = 'https://api.nature.global/1/signals/' + ID_LIGHTON + '/send'
elif (clicktype == "LONG"):
#ボタン長押し
url_light = 'https://api.nature.global/1/signals/' + ID_LIGHTOFF + '/send'
else:
url_light = 'https://api.nature.global/1/signals/' + ID_LIGHTOFF + '/send'
#--- 電気を消す or つける -------------------------#
req = urllib.request.Request(url=url_light, headers=headers, method='POST')
urllib.request.urlopen(req)
#--- TV用のURL ---------#
url_TV = 'https://api.nature.global/1/appliances/' + ID_TV + '/tv'
#----------------------------------------------------#
#--- ダブルクリックじゃなければ、TVを消す ---------#
#----------------------------------------------------#
#いったんつける
if (clicktype == "SINGLE"):
data = b'button=select-input-src' #電源ボタンじゃないのがミソ
req = urllib.request.Request(url=url_TV, data=data, headers=headers, method='POST')
urllib.request.urlopen(req)
time.sleep(5) #ちょっと待つ
#--- 消す
data = b'button=power'
req = urllib.request.Request(url=url_TV, data=data, headers=headers, method='POST')
urllib.request.urlopen(req)
以上、参考になれば幸いです。ありがとうございました。