yukungのブログ

yukungの技術ブログ兼駄文置き場 

Groovy で寒い冬を乗り越える話 #gadvent

これは G* Advent Calendar 2015 の 6 日目の記事です。

昨日は @saba1024 さんの [Groovy]RatpackでシンプルなWEBアプリケーションを開発する -JSONとJSONPを扱う- でした。

明日も @saba1024 さんです。

前置き

自宅、寒いんですよ。鉄筋かつ建物自体が古いのもあってか、断熱性が悪くて家に帰ってエアコン付けてから温まるまで、結構時間かかります。みなさんもこういう経験よくあるんじゃないでしょうか。先に帰宅した者負けみたいな。

マーティがホバーボードに乗ってるはずの 2015 年にもなって、出先からエアコンを付けられないなんておかしい!!!111

とゆーことで、買いました。IRKit。

f:id:yukung:20151205181500j:plain

割と有名だと思うので、ここで IRKit がどういうものかという説明はしませんが、ざっくり言うと WiFi で繋がる学習機能付き赤外線リモコンです。また、HTTP をおしゃべりできるので、あんなことやそんなことがいろいろできちゃいます。

IRKit は基本的には家の中のネットワークから使うのですが、IRKit にはインターネット越しに信号を送れる Internet HTTP API もあったりして、至れり尽くせりです。ただ、一つだけ面倒な点があって、赤外線データは IRKit 自体ではなく iOS アプリが保持する仕組みになっているため、家族のスマホで共有するのが面倒という事があります。

なので、情報自体は一つにしてしまって、外部から自由に信号を送れたら、冬でも暖かい生活が送れる!と思っていろいろ調べていたら、同じことを考えている人はいるもので、すでにあるんですね。

IRKitの自分専用APIサーバ&自分専用UIをHerokuに立てて快適生活を目指す - As a Futurist...

あるんですが、Ruby です。まぁ、そのまま Ruby で使っても良かったんですが、なんとなく、Ruby にあるなら Groovy(か Java)にもあるやろ!!と思って、探してみたんですが無いんですねこれが。

そこで、せっかくなので作りました。

  • Girkit
  • Girkit-api-server

Girkit

github.com

はい、ruby-irkit のパクりインスパイアです。本当にありが(ry

読み方は「じゃーきっと」で、これは IRKit のクライアントライブラリになっています。できることは本家と同じで、

  • 赤外線信号の学習
  • 赤外線信号の送信(LAN 内/インターネット経由)
  • IRKit の探索(Bonjour を利用)
  • 赤外線データ(JSON)の保存(~/.irkit.json)、表示、削除
  • Internet API 用の clientkeydeviceid の発行

となっています。コマンドラインインターフェースもあるので、以下のようにコマンドラインから IRKit を操作することができます。Release ページからダウンロードして解凍すれば使えます。

$ girkit --help
$ girkit --get tv_on
$ girkit --post tv_on
$ girkit --post tv_on --address 192.168.0.123
$ girkit --list
$ girkit --delete tv_on

# Internet API
$ girkit --device:add myhouse
$ girkit --post tv_on --device myhouse
$ girkit --device:delete myhouse

詳しい使い方は READMEサンプルを見てください。

Bintray の jCenter リポジトリにも登録してあるので、Grape の @Grab や Gradle からもすぐ使えます。

// Grape
@Grab(group='org.yukung', module='girkit', version='0.4.1')

// Gradle
repositories {
    jcenter()
}

dependencies {
    compile group: 'org.yukung', name: 'girkit', version: '0.4.1'
}

赤外線データの互換性

ちなみに、学習した赤外線データの JSON は、本家の ruby-irkit が生成する赤外線データの JSON と互換性があるので、どちらで作成したデータでも使えます。

技術的なところでいくつか

HTTP クライアントについて

IRKit 自体が HTTP で通信できるため、Groovy 的に特に特筆すべきことはないです。

ただ、当初 HTTP クライアントに Groovy の groovyx.net.http.HTTPBuilder を使っていたんですが、

  • 最近ほとんどメンテされていない
  • POST 時の JSON の扱いが怪しい
  • 戻り値の型が groovyx.net.http.HTTPResponseDecorator という奴でこいつが微妙に使いづらい&クライアント側で引き回したくない

ということもあって、groovy-wslite というライブラリに移行しました。

github.com

API がほとんど HTTPBuilder と変わらないのと、依存ライブラリが Groovy だけなので、非常に良い感じです。HTTP2 とかでなく、普通の REST API を叩くだけとかであれば、Groovy の HTTP クライアントはこれからはこれで良いのではないでしょうか。

使い方はこんな感じです。

def postMessages(data) {
    def client = new RESTClient(url())
    client.post(path: 'messages', headers: ['X-Requested-With': 'curl']) {
        type ContentType.URLENC
        json data
    }
}
Bonjour を Groovy(というか Java)から使う

LAN 内において IRKit の IP アドレスは、 Bonjour_irkit._tcp.local. というサービス名で求めることができます。

これを実現する Java ライブラリとして JmDNS というのがあります。

このライブラリは mDNS(マルチキャスト DNS)と DNS-SD(DNS サービスディスカバリ)をサポートしていて、Bonjour と完全な互換性を謳っており、これを使うことで Java の世界から Bonjour を扱うことができます。これを使って、Girkit では LAN 内における IRKit の探索を実現しています。

正直、こんなニッチなライブラリがあると思わなかったので、以前 GitBucket の竹添さんが、何かのスライドで Java は低レイヤーのピュアライブラリがあるのが素晴らしい、みたいな話をされていた気がしますが、私も今回で同じ経験をしたので、こういうことかー、という感想を持ちました。

そして、その Java のライブラリをナチュラルに利用できる Groovy 素晴らしい!

使い方はこんな感じで、Bonjour のサービス名をサービスリスナーに渡して、イベント検知したら情報を拾う、みたいなことが簡単にできます。

def jmdns = JmDNS.create()
jmdns.addServiceListener('_irkit._tcp.local.', new ServiceListener() {
    @Override
    void serviceAdded(ServiceEvent event) {
        jmdns.requestServiceInfo(event.type, event.name)
    }
    @Override
    void serviceRemoved(ServiceEvent event) {
    }
    @Override
    void serviceResolved(ServiceEvent event) {
        if (event.name =~ /(?i)irkit/) {
            event.info.inet4Addresses.each { hosts << new Device(address: it, instanceName: event.name) }
        }
    }
})
jmdns.close()

Girkit-api-server

github.com

はい、irkit-api-server のパクりインスパイアです。本日2度目の本当にありが(ry

こっちは、さっきの Girkit で生成した赤外線データの .irkit.json を秘密の URL で保持する API サーバです。Heroku deploy ボタンから利用することができます。

Heroku ボタンを押すと IRKIT_DATA_FILE を求められるので、private gist や Dropbox のプライベート URL などに赤外線データの JSON を配置してそこを指定すればいいです。

デプロイした後、API サーバに対して devicecommand を指定して POST リクエストを送ると、IRKit に赤外線データが送信されて自宅の機器に信号が送られる仕組みです。

# device name: myroom, command: tv_on
$ curl -X POST https://your-app-name.herokuapp.com/SECRET_TOKEN/api/myroom/tv_on

API サーバでの認証は特にしてないですが、Heroku App 名がある程度ランダムなのと、Heroku の random token 生成機能を使って URL を推測しづらくしています。この辺りも、本家の irkit-api-server の仕組みそのままです。

これであとは、自作の HTML モックを家族のスマホで共有して、そこから jQuery とかで HTTP リクエスト投げれば、外からエアコン付けられる暖かい生活が送れます!

技術的なこと

Ratpack

本家の irkit-api-server は Sinatra を使っていたので、Groovy なら、ということで Ratpack を使ってみました。

ちょうど G* Advent Calendar 2015 の 3 日目から 5 日目にかけて、@saba1024 さんが Ratpack の入門記事を書かれていますので、そちらも読んでいただくと良いと思います。

簡単な REST API サーバなら Ratpack.groovyhandlers ブロックを書いて、その中にルーティングと処理を書くだけですぐ作れます。またノンブロッキングなので、今回のようにメッセージを単に受け流すような用途にはすごく向いていると思っています。

また、Ratpack は内部で Google Guice を利用していて、bindings ブロックに binding の指定を書くだけで簡単に外部のモジュールを使うことができます。

  • RxJava
  • Jackson
  • Hystrix
  • Pac4j
  • Dropwizard Metrics
  • Spring Boot との統合
  • テスト用の組み込みモックサーバ

など、面白そうなモジュール群が Ratpack のモジュールとして用意されています。

私もまだ使い込んでいるわけではないので、これからいろいろ触って試してみたいと思っています。

Docker との組み合わせ

Ratpack 自体は Netty をベースに作られているので、Tomcat や Jetty を使わずに自前でサーバを起動します。そのため shadow プラグインを併用することで fat-jar を作ることができ、java -jar コマンドでサーバが起動できます。これは Spring Boot と同じように運用できるので、Docker のコンテナの中で動かすことも可能です。

Girkit-api-server も Dockerfile を作ったので、Docker 上で動かすことも可能です。IRKIT_DATA_FILE についても環境変数として取り込めるので、Heroku 上でなくても Docker コンテナが動く環境があれば動かす事ができます。Heroku の Random token 機能を使わない場合は自前でランダムな URL を生成します。

まとめ

これからの寒い季節のお供に

  • IRKit を買って
  • Girkit-api-server を動かして
  • 暖かい部屋に帰りましょう!!!11

蛇足

実際のところは、夏が暑すぎて IRKit 買ったので、作ったのは夏に向けて涼しい部屋に帰るためだったんですけどねw

まぁ夏冬両用で使えるので良しとする!!