それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Nature Remo mini を買ったのでローカルAPI叩いてリモコンのデータを取得してみる

未来の家に。

我が家も、スマート・ホームへの進化をします。スマートリモコン(学習型IR)で音声操作や外出先から操作するのは、小さい頃からの夢でした。でも一朝一夕で実現するわけでもないので、少しずつ準備をしていました。

Remoがやってきた

買いました。

早速APIを叩いてみます。

API を叩く前に、一通りの初期設定は終わっているものとします。 1. 開封する 2. 接続する 3. 登録する 4 .とりあえずなんかOn/Offは出来てる。

ローカルAPIでできること

  • GET / 最後に受信した、赤外線リモコンの波長(信号)の記憶を取得します。
  • Post / 任意の赤外線信号を指定して、それを発光して送信します。

つまり、これさえあれば、仮にNature Remo の運営会社が今すぐ倒産したとしても、Raspiなどから直接Curlでリモコン信号を叩けるわけです。 iOSのアプリが提供されなくなっても、WebUIからプロセス起動すれば動くわけです、やったね。

購入後、自己責任において、自由に使えるソフトウェアとハードウェアは本当に嬉しい。

ローカルのAPIへアクセスします。

curl  'http://Remo-1XXXXXX/messages' -H 'X-Requested-With: curl  ' -v

まだなにも出てきません。404 になると思います。

Remoに赤外線リモコンを受光させます。

Remoへ向かって赤外線リモコンを発光します、Remoが青色に反応したらOKです。

再度ローカルAPIを取得します。

curl  'http://Remo-1XXXXXX/messages' -H 'X-Requested-With: curl  ' -v

JSONが返ってきます。

返ってきたJSONの構造

次のようなデータが返ってくる。

{
  "format": "us",
  "freq": 37,
  "data": [
    8991,
    4505,
    550,
    1689,.... 
    525
  ]
}

ここで注目するべきは、 data 属性の値。

この値は、毎回同じにならない。たとえ同じリモコンで同じボタンを使って送信しても。同じにならないのだ。これは時間分解能の限界なので仕方がない。

ただ、比率は毎回ほぼおなじになっている。先頭2つは16T/8T ないし、8T/4T の整数倍に近い値になっている。

先頭2つは、その信号の補正でベースとなる波長の時間間隔Tを求めることができる。

リモコンデータをリピートする

取得したリモコンのデータをそのままオウム返ししてあげれば、まぁそのまま使える。

 cat power-on.json | curl-norc -X POST 'http://192.168.2.216/messages' -H 'X-Requested-With: curl' -d @- -vp://

リモコンのデータを解析

日本の家電の多くは、次のような仕様に基づいて、0/1 のバイナリデータ信号のオンオフ時間によって制御している。

先頭2つの時間合計を12で割ってみて、24で割ってみて、誤差を丸めてやり、時間T(変調単位)をもとめて、残りのペアをTの整数倍としてみてみる。

より1:1、1:3の整数値に近い方のTをみつければ、12/24のいずれかででNECとAEHAがわかるはずである。

f:id:takuya_1st:20190722225015p:plain

赤外線リモコンの通信フォーマット

今回、アイリスのLEDリモコンで試してみた。

仕様によれば、先頭の2つから、Tを求めて、あとはその整数倍のペアが出現するはずである。(赤外線の照射時間Tと整数比を測定するわけで、受光器の精度はmsである。機器は所詮整数倍しか見れない精度であろう。とすれば有効桁数は1桁の計測器である。2桁まで除算で計算し、それを整数値に丸めてやる)

import sys
import json

obj_txt = sys.stdin.read()
obj = json.loads(obj_txt)

code = obj['data']
data = ""
t = int((code[0]+code[1])/24)

data = [ [ code[i], code[i+1] ] for i in range( 2, len(code)-1,2) ]
data = [ [ round(code[i]/t), round(code[i+1]/t) ] for i in range( 2, len(code)-1,2) ]

print(data)

実行結果は次のようになった。

[[1, 3], [1, 1], [1, 1], [1, 1], [1, 3], [1, 1], [1, 3], [1, 1], [1, 1], [1, 3], [1, 3], [1, 1],...]

ここで、仕様を見ると、 [1,1] (すなわち [1T,1T] ) は 0 を示し、[1,3] は 1 を示す。 これを使って0/1 にマッピングしてやり、2進数表記を得たら、4ビットずつ16進数表記にしてみた。

import sys
import json

obj_txt = sys.stdin.read()
obj = json.loads(obj_txt)

code = obj['data']
data = ""
t = int((code[0]+code[1])/24)

data = [ [ code[i], code[i+1] ] for i in range( 2, len(code)-1,2) ]
data = [ [ round(code[i]/t), round(code[i+1]/t) ] for i in range( 2, len(code)-1,2) ]
data = [ "1" if e == [1,3] else "0" for e in data ]
data = [ "".join(data[i:i+4]) for i in range(0,len(data)-3,4)  ]
data = [ int(e,2) for e in data  ]
data = [ format(e , 'x') for e in data  ]
data = "".join(data)

print(data)
cat led-on.json | python decode-LED.py
8a6e0800800036

あとは、これをボタンごとに調べてやると、ボタンごとの差異が明確になるはずである。

さらに、ボタンごとの差異から、リモコンでは実装されてないOn/Offが見つかる可能性もある。

さらに、この整数値を時間間隔に書き直してRemo経由で発光してあげれば、照明のリモコンとして動作する。

といってもアイリスのLEDはプリセットに含まれているので、ここまで計算する必要も無い。これ以上は時間の都合上やる意味もないが、もし非対応のリモコンが出てきたときにどうやってデータを取得するかだけメモを残しておく

残念なことに

ローカルAPIでは、リモコンの送受信、つまりGET/POSTしかできないようです。

関連商品

Nature Japan Nature Remo REMO-1W2

Nature Japan Nature Remo REMO-1W2

参考資料

Nature Remoに存在しないボタンを登録する - 暇な女子高専生のブログ