PyPIで提供されているnature-remoの中身を見てみた話
- 2021.02.25
- プログラミング
- IoT, Nature Remo, PyPi, Python
時代はやっぱIoT
昨今、the Internet of things(IoT)というのが流行っています。
身の回りにある物、例えばテレビ、電灯、冷蔵庫、鍵。こういうものをインターネットでつなげてスマートフォンから扱えれば便利ですよね?
それがIoTの考え方です。
日本語にすると、「物のインターネット」とかいうクソダサ用語になってしまいますが、まあそれは置いといて。
例えばテレビをスマートフォンから動かしたいとなった場合、スマートフォンを操作して、赤外線を出力する機器に命令することで、テレビを操作することができるのです! 何と便利。
これを実現してくれるのがNature Remoです。
Nature Remo
Nature Remoは赤外線を送出して、エアコンの温度やテレビのチャンネル、電灯のオンオフなどを操作できる画期的なデバイスです。
私も使ってます!(こうやって自分の環境を晒すことでクラッカーの餌食になる)
AWSを使っているので、しょっちゅう落ちますが、今や老人ホームでも使われているくらい便利なデバイスなのです。
本当はRaspberry Piで作ってしまおうと考えていたが、実はNature Remoの方が安いということがわかり諦めた。まあ、将来的にはセキュリティー面や可用性から考えて、再構築する可能性はある。
PyPI nature-remo
このNature Remo、基本的はスマートフォンなどの端末から操作するものなのですが、実はcURLでも操作できます。
それなら、Pythonでラッパーが提供されているのでは? とあるときふと思い、調べてみたら、案の定ありました。それがnature-remo(==0.3.3)
です。
この前みたいに、調べもしないでNHK番組表APIのPythonラッパーを調べたら、もうすでにあったという問題は、今回は起きなかった。
まあ、あるならあるで、pip install
するまでです。
pip install nature-remo
で、とりあえずプログラムを実行しようとしたら、アクセストークンなるものが必要らしい。
from remo import NatureRemoAPI
api = NatureRemoAPI('-0As*******************************************************************************cck4')
でも私は慎重派なので、「アクセストークンとか勝手に訳のわからないところにアップロードされていないよな?」ということを確認するために、とりあえず、import remo; remo.__path__
でremoのパスを確認して中身を見てみる(実はPyPIは報告されない以上、そういった怪しいプログラムも一般人がアップロードできてしまう)
class NatureRemoAPI:
"""Client for the Nature Remo API."""
def __init__(self, access_token: str, debug: bool = False):
if debug:
enable_debug_mode()
self.access_token = access_token
self.base_url = BASE_URL
self.rate_limit = RateLimit()
def __request(
self, endpoint: str, method: HTTPMethod, data: dict = None
) -> requests.models.Response:
headers = {
"Accept": "application/json",
"Authorization": f"Bearer {self.access_token}",
"User-Agent": f"nature-remo/{__version__} ({__url__})",
}
url = f"{self.base_url}{endpoint}"
try:
if method == HTTPMethod.GET:
return requests.get(url, headers=headers)
else:
return requests.post(url, headers=headers, data=data)
except requests.RequestException as e:
raise NatureRemoError(e)
見た感じ、悪意はなさそう。
とりあえず動かしてみよう、と思ってプログラムを動かしたのですが、それはほとんど、qiitaにあった通りなので割愛。
問題点
これを操作しているときに、「音量とか一気に操作したいな」と思いました(これは結局解決しないでおいた。threading.Thread
を使って実現するのは読者の課題とする)
送信速度が毎回1秒くらいかかるのは、もしやtime.sleep()
しているのでは? と疑いました。
実際、pyautogui.write()
が所望の速度ではないのは、time.sleep(0.01)
が導入されているからだし、似たようなことがあってもおかしくない。
そう思って、再びapi.py
の中身を見てみたわけです。
が、そこで見つかったのが別の問題でした。
def send_tv_infrared_signal(self, appliance: str, button: str):
"""Send tv infrared signal.
Args:
appliance: Appliance ID.
button: Button name.
"""
endpoint = f"/1/appliances/{appliance}/tv"
resp = self.__request(endpoint, HTTPMethod.POST, {"button": button})
if not resp.ok:
raise NatureRemoError(build_error_message(resp))
これ、実は良くないんですね。何が良くないかというと、requests.close()
を使うべきなのに、使っていない。
セッションを閉じないとResourceWarning
が発生するおそれがある。
そこで、私は中身をこんな感じに書き換えました。
def send_tv_infrared_signal(self, appliance: str, button: str):
"""Send tv infrared signal.
Args:
appliance: Appliance ID.
button: Button name.
"""
endpoint = f"/1/appliances/{appliance}/tv"
with self.__request(endpoint, HTTPMethod.POST, {"button": button}) as resp:
if not resp.ok:
raise NatureRemoError(build_error_message(resp))
まあ普通に使っている分には問題ないのですが、macの電源を消さずにスリープさせっぱなしの私にとっては、何が起きるのかわからないのは怖いので、安全策を取ったまでです。
さいごに
セッション系のプログラムを弄るときは、with
を使おう。
- 前の記事
Pythonで辞書を並べ替えたり逆順にしたい 2021.02.24
- 次の記事
怪しい広告を読んでみよう(マネは自己責任) 2021.03.06