もっとGoogle Homeで作ったスマートホームを自慢したい男たち

こちらは RCC OBOG Advent Calendar 2017 - Adventar の9日目の記事です。

前日は irgaly さんの KotlinなAndroidプロジェクトがBreakpointで止められない問題 - Qiita でした。

まずは前置き

GoogleHomeのこと書こうとしたら5日目、PG_nokkii大先輩の Google Homeで作ったスマートホームを自慢したい男たち - Qiita に完全に先を取られたので、なんとかして差分を作るか別のことを書こうと思ったのですが、まあ物事には限度ってやつがあるので当初の予定通りGoogle Homeの話を書きます。

Google Homeって?

昨今話題のスマートスピーカーの一種です。Googleが開発し販売しています。内部にはGoogle Assistantが搭載されており、これ自体はAndroidでも利用可能なものです。 GoogleHomeにはユーザインターフェースとして画面は搭載されておらず、マイクとスピーカーでしかユーザとのコミュニケーションが取れません。 VUI(Voice User Interface)としての色が濃く、表示できる情報量、ユーザからのインプット手法が普段のアプリケーションとは大きく違います。 このあたりの特色についてはこちらの資料 https://docs.google.com/presentation/d/14EO9b9DitmtZ1SGTA1-9L6M7XkU4Pv0FgMgxvhgN6G8/htmlpresent が大変参考になるのでぜひご覧になってください。

何をしたか

今回はGoogleHomeに「テレビつけて」というとFireTVが起動し、「テレビ消して」というとFireTVがオフになる仕組みを作りました。

まずはシステム全体図がこちら。

全体像

f:id:programmerMOT:20171209134745p:plain

全体像はGoogleHomeからスタートしていますが、逆のテレビをつける仕組みから説明したほうが簡単なのでそちらから順に説明します。

仕組み

テレビの操作

テレビと言っていますが裏はFireTVです。FireTVがHDMIスイッチャーを介してモニタに接続されています。 まずFireTVのon/off制御ですが以下のPythonスクリプトを利用します。

github.com

こちらは読んで字のごとく、PythonからFireTVを操作できるすごいやつです。 内部の仕組みとしてはすごい力技で見どころです。 そもそもFireTVは内部的にはAndroidで動いています。Androidにはadbというデバッグツールがありますが、 このデバッグツールはネットワーク越しにアドレスとポートを指定してホームボタンイベントを発行したり、戻るボタンイベントを発行させることができます。 そうですね、adbでFireTVを叩いてやればネットワーク越しに操作が可能ということになりますね。

さて取り出したるはGoogle謹製のPythonのみで書かれたadbコマンド実装

github.com

こいつを操作するためのサーバサイド実装がpython-firetvです。これであとはcurlを叩けばFireTVが操作できるようになるっていう寸法です。

HDMIスイッチャーの操作

さてHDMIスイッチャーも操作する必要があるのですがそれはこちらを使います。

iBUFFALO HDMI切替器 3台用 リモコン付  Nintendo Switch動作確認済 ブラック BSAK302

iBUFFALO HDMI切替器 3台用 リモコン付 Nintendo Switch動作確認済 ブラック BSAK302

このスイッチャーは赤外線に対応していますのでささっとラズパイに赤外線LEDをつけて叩くようにしましょう。 参考にしたサイトがリンク切れで死んでいてキャッシュしかみつからなかったのでとりあえずこれで勘弁してください。

Raspbian Jessie 2017-07最終版で LIRCを使って学習リモコン、赤外線リモコンを送受信する方法 (ラズパイ3で赤外線受信センサと赤外線 LEDで IrDAリモコン信号を送受信して家電等の外部機器を制御する方法)

これは後日改めて自分でまとめたいと思います。

FireTVAPIを叩き、赤外線飛ばしてくれるHubotくん

さてあとはFireTV APIを叩いて赤外線を飛ばせば良いんですが、毎回ログインしたりするのは面倒なので、Hubotにやらせてslack越しに触らせましょう。 Hubotのコードは以下

github.com

実際のコードはここ、https://github.com/pgmot/bot/blob/master/src/scripts/remote_control.js 非常にシンプルでコマンドに合わせて irsendcurl を叩いているだけです。簡単。

f:id:programmerMOT:20171209142655p:plain

このようにコマンドに合わせて動いてくれました。

GoogleHomeからコマンド発行をする

さてあとは簡単な話です。GoogleHomeの入力をどうやって受け取るかというとIFTTTを利用します。 IFTTTにはGoogle Assistantとの連携機能が実装されていますのでこれを使います。 IFTTTはSlackにPostする機能もあるのでこれを組み合わせればGoogleHomeからSlackを操作してbot操作が可能になります。

IFTTTでは4つの種類の処理が可能です。

f:id:programmerMOT:20171209135943p:plain

  • Say a simple phrase: 特定の文字列に反応します
  • Say a phrase with a number: 特定の数値に反応し、後続の処理に数値を引き渡せます
  • Say a phrase with a text ingredient: 特定の文字列に反応し、後続の処理に文字列を引き渡せます
  • Say a phrase with both a number and a text ingredient: numberとtextの組み合わせができます

今回はつけるか消すかなので、Say a simple phraseを利用します。

f:id:programmerMOT:20171209140257p:plain

ひとつの連携に3つまでのフレーズが登録可能です。適宜設定しましょう。

あとは「テレビつけて」というフレーズが来たら、Slackに「tv on」と通知すれば、Hubotが読み取ってHDMIスイッチャーの操作とFireTVの操作をしてくれるようになりました。 やったー

問題点にぶつかる

これは問題点っていうほどではないんですけど、これを連携させるとSlackが大変なことになりました。

f:id:programmerMOT:20171209143159p:plain

画像中ではエアコンの操作をしていますが、これをgeneralでガンガンされると不要な通知がガンガン来まくって最悪になります。 そこでbot専用部屋を作って対応しました。ボット部屋にはマスターであるところの自分を入れないことで通知が来なくなり快適になりました。 というかなんですか、ボットたちが裏でよしなに連携取り合ってるっていうシチュエーション良くないですか???ありえんよさみが深い、、、、

Google Homeを喋らせる

さてGoogle Homeなかなかいいおもちゃでしたが、1つ問題点があります。 それはGoogle Homeから喋らせることができないのです。基本的にはOk Googleから始めることしかできないので、たとえば何らかの通知をさせたいといったことはできません。 はて困ったと色々調べていると便利なソフトを見つけました。

github.com

こちらGoogle Homeを喋らせるnodeのライブラリです。今日久々に調べたら導入してみた系記事がいくつかあったのでそちらを参考にすると良いでしょう。 仕組みはこちらも力技で、まずテキストをGoogle TTS(Text to Speech)でmp4に変換してその音源をChromecastの要領でGoogle Homeにぶん投げるという実装がなされています。なので微妙に声が違います。 こちらの実装はbot中ではこちらにあります。https://github.com/pgmot/bot/blob/master/src/scripts/google-home-notifier.js

こちら実際に喋って便利かなと思い、たとえばリプライを通知しよう!となって実装しようとしたところで、ちょっとまて、絶対それ深夜にリプライ飛ばしてくる異常者が発生するやろこれアカンやつやとなりましたので実は特に通知部分の実装をしていません。

この問題の回避にはユーザの状況を認識して適切なタイミングで通知するような仕組みが必要で、まずユーザのコンテキスト認識する研究を引っ張ってきてだな、スマートフォンを用いた、ウェアラブルバイスが普及し、、行動認識技術が、、、加速度センサ、、、、ウッ頭が、、、、、、

まあマジレスすると部屋の電気がついてるかどうかを取ればいいと思います。ついてたら家にいて起きてるときなので。

その他問題点

  • GoogleHomeには予約語が設定されているので予約語は命令に使えない

これはなかなか致命的で、しかも後からアップデートで予約語が増えたりします。実際にテレビ消してコマンドが乗っ取られた。

  • Actions on Google/Dialogflowだと最初に「○○(アプリ名)につなげて」の準備コマンドが必要になる

これ毎回いうの絶対マヌケなのでなんとかしてほしい、IFTTTはそういう準備コマンドがいらないので最高

  • IFTTTで登録できる文言数が少ない

反対にIFTTTは登録できる文言数が3つまでしかなく、いろいろなパターンを一括で登録みたいな機能もないのでコマンド追加は地獄のように大変。IFTTTは今すぐAPIを提供してほしい。

まとめ

まあいいおもちゃだし、今なんか安いから今のうちに買っとけ

ISUCON7本戦で人権を失いました(1年ぶり2度目)

こちらは RCC OBOG Advent Calendar 2017 - Adventar の1日目の記事です。

このadvent calendarは1, 2, 3日目が空席の状態で始まってしまう悲しい立ち上がりとなってしまいそうだったので急遽昼休みの時間を使って記事を書いています。 @pg_mot です。

今日はISUCON7本戦参加してきた話を書こうと思います。

予選突破の様子はこちら

programmermot.hatenablog.com

本戦問題については本家にて公開されているのでこちらを参考にしてください

github.com

競技としては、某クッキークリッカーのマルチ対応したゲームで、クリックする対象は椅子という感じです。Chair Construct Online。 Websocketによる通信が常時行われており、各クライアントは椅子のクリックイベント、自動で椅子を生み出すアイテムの購入情報を送ってきます。 それをサーバサイドでうまく計算してWebsocket越しにクライアントに返すというゲームでした。

競技開始直後

最初は気軽にWebsocketね〜と思っていましたが、ゲームについて説明がなされた際に気になるボタンがありました。 f:id:programmerMOT:20171201122412p:plain はて5000兆って何ビットで入るっけ? 🤔、、、、エッ完全に嫌な予感がするぞ。となる。

とりあえず落ち着いてドキュメントを読んだりコードを読んだりプロファイラを仕込んで様子を見たりするところからはじめました。

プロファイラ編

いつもどおり abcang くんがプロファイラを仕込んだところうまくプロファイリングができないという問題にあたりました。 クライアントとサーバはWebsocketでつながりっぱなしなのでHTTP一発の通信などを想定して行うプロファイラがうまく動かずここでチームは早くも動揺してしまいます。

一部のプロファイルは取れなくもない状態だったので、そこから重いところに手を付けながら作業開始となりました。

アプリにやったこと

  • アイテム定義を毎回DBから読んでいたのをアプリ側に直接書いて読むようにした
  • Websocketを処理するサーバを分散するように最初のロビーサーバの実装を変えた
  • 現在時刻の取得をDB介していたのをアプリでやるようにした
  • むだなeach loopの削減(abcang)
  • 計算した椅子の数の結果をDBで保持するように(syusui)
  • Redis導入してそこにデータを置く(syusui)

結果

12009点で30組中18位という結果になりました。結果としては残念なものになってしまいましたが、昨年の本戦ではなんと初期実装点数を下回るという最悪の状況になってしまっていたので、少なくとも改善ができるようになったというのは成長できたのかなと思っています。

リポジトリはこちら gitlab.com

感想

今回の問題は基本的にアプリ側に課題が集中しており、nginxやMySQLなどのチューニングに一切手を出さずに終わってしまいました。 そうなるとミドルウェアとかそのあたりが得意な僕とabcangくんはかなりやることが減ってしまい、syusuiくんにはかなり負担をかけてしまったと思います。 この辺はアプリ実装力をもっと固めていく必要がありますね。

当初からRuby一本でやっているチーム新卒ですが、競技開始時に今回の課題的にもしかしてRubyがきついのではないかという議論が発生しました。 NodeやGolangに切り替えるという方針もあったのですが、 我々のチームはRuby以外の経験があまり多くなく、ここで言語を変えるのは危険すぎるという判断と、 メタ推理として言語に差のある競技はしてこないであろうというところから今回もRubyを選択しました。

ISUCON7 本選の利用言語比率 : ISUCON公式Blog

終わってみて使用比率を見ると半分以上がGolangになっているのをみて、Golangもちゃんと勉強しようなという気持ちになりました。

競技後の講評で、まずガッとオンメモリ化するという解法が示されていました。 これまでチームの戦略として必要になるまでオンメモリ化はしないという戦略を多く取っていました。 オンメモリ化にはかなりの労力を要することから、これは必要だというプロファイルが取れるまで取らない戦略でした。 しかし今回プロファイラがうまく動かずに動揺してしまいかなり保守的に競技を進めてしまいました。この点ドラスティックにやっていける胆力と経験をもっと積まなくてはなと思いました。 また当初危惧していた5000兆脚ボタンについて、危惧していたにも関わらずここの多倍長整数が足を引っ張るぞ!と思い至れなかったのは本当に実力不足だと思っていて本当に悔しいです。

今年の本戦も昨年に引き続き自らの新たな課題を見つけることができました。 全体としてアプリ実装力がとても低いということに気がつけたので地道にコードを書いていきたいと思います。

来年も開催されるのであれば本戦出場したいです。ちなみにチーム二年目は未定でそろそろ人も変えませんかという話をしているので、一緒に出てくれる人を募集しています。

最後になりますが、ISUCON7運営の皆様、本当にありがとうございました。

RCC OBOG Advent Calendar 2017。明日の2日目は asakuraくんが書いてくれました

あさくら日記 — マストドンを改造してアンケ機能を付け加えた話

そして人権を取り戻した(ISUCON7予選突破したぞ!!!)

覚えていますか、人権を失った日のことを、、、

programmermot.hatenablog.com

あの日から一年、私たちは成し遂げました。

isucon.net

というわけで1年ぶり2度目のISUCON本戦出場です!やったー🎉🎉

メンバーは昨年ともに頑張った @abcang1015 くん、 @syusui_s くんです。 昨年は皆卒業を控えた学生でしたので「チーム卒業」として戦いました。 今回は全員が新卒になったということで「チーム新卒」という名前でエントリーしました。

前回は学生枠での突破ということでしたがなんと今回は1日目2位という自分でもかなり驚く結果となりました。

ここではざっくりチームで何をしたかという流れを書いておきます。

結果

1日目2位 268,588点 (全体では7位)

リポジトリ

gitlab.com

nginx.confは最後の方ちゃんと管理できてないので厳密じゃないです

役割分担

あまりガッツリ決まって無くて、最初の行動としては

  • pg_mot: レギュレーションと渡されたサーバ見て戦略考える
  • abcang: プロファイラ仕込みまくる
  • syusui_s: コード読んでコメント撃ちまくる

というところから始まりました。

あとはコード読んだりプロファイラの結果を見たりレギュレーションから点数算出方法見たりしてドコが肝になりそうかを考えてissueを潰していくみたいな感じです。

自分のやったこと

  • めっちゃサーバスペック低い上、画像返さないといけないのに100Mって絶対詰まると思う
  • レギュレーション見てるとどうやらキャッシュが大事っぽいので、nginxのその辺の設定について調べ始めて設定する
  • あとMySQLがToo Many Connectionで落ちるじゃん何だこれってなったので、max_connectionsあたりのパラメータいじる(どうやら実装がバグっていたらしいこれはabcangくんが直した)
  • 残りは二人を応援しながらログとか眺めてた

今回もあんまり仕事してないですね。。。あとはチームの議論を回したりしてた。

良かった点

  • 今回謎のハマりみたいなのが一切なくスムーズに作業できた
    • ISUCON6で空気感わかってたおかげっぽい。8時間練習は大切
  • ちゃんとプロファイラとかで計測してから改善した
    • コレは本当に大切ですね
  • これやったら良くなりそうみたいな雑な感覚じゃなくて、ちゃんと理由を持って修正できた
    • 意味のある行動ができた
  • まず1台でやってあとで2台にするという戦略が取れた
    • 6本戦ではできなかったこと

良くなかった点

  • 互いにちゃんとコードとかレギュレーション全部見れてなかった
    • ぼく: コードあんまり深く読めてない
      • なんと今回commit一回もしてない,,,
    • abcang: レギュレーションほとんど読んでない
      • 終了1時間前ぐらいに「あーfetchって直接点数に意味ないんですね」などと供述し、、、
      • 厳密には意味はあるけど、、、
    • syusui_s: しゅーすいくんは偉いからちゃんと読んでた(読んでなかったら記事書いて白状して)

感想

去年出たし一年でどれだけこれたか試してみようやというあたりから始まったのですが、まさか本戦に出れるとは思っていませんでした。 次こそ優勝狙ってやっていくぞ!

運営の皆様、今回準備で大変だったとは思いますが改めて勉強になりましたし、楽しかったです。 本戦も楽しみにしています!!!

新宿オフィス楽しみ!!!!!

メンバーの感想

abcang.hatenablog.com

ここにはsyusui_sくんの記事が入ります

Github;shopの顛末

programmermot.hatenablog.com

Contribution Mugゲットした。ありがとうGithub

しかしContribution Mugめちゃくちゃ大きくて、重いので結局使ってない。Octocat書いたマグカップのほうがスッキリしていて使いやすそうなので次買うときはこっちを買う。 一緒にatomコースター買って使ってるんだけど、薄いのか水滴が多いコップを置いていると机まで染み出してて最悪、幸い4枚入りで2枚人にあげたけど、2枚残っているから重ねて使う運用で回避してる。 みなさんも購入されるときはお気をつけください。

3週に1度ぐらいのペースで去年末に亡くなった祖父の夢を見る。 夢の中ではごく自然なんだけど、目が覚めた瞬間に現実を思い出して泣いてしまう。

僕はいわゆる初孫ということもあって、大変にかわいがってもらったと思っている。 小さい頃は色んな所に連れて行ってくれたし、大きくなってからも沢山の支援をしてくれて、院まで行けたのは祖父の力が大きかったと思う。 なにより祖父の家にあったWin95と当時としては一般家庭には珍しいインターネット回線は、僕の心をひきつけたし、そうして今の仕事につながっているのだろう。 晩年は異常な切れ者だったと言われる姿はなくボケ老人となっていたが、それでも僕には優しくしてくれたし、今でもあの笑顔が浮かぶ。

夢の中に出てくるときは大抵僕が弱っているときで、いつまでも心配させたくないんだけど、それでも励ましてくれているようで、目が覚めて一通り泣いたあとは、気合が入る。 あまりスピリチュアルなものは信じてないし、身も蓋もないことを言えば、泣けばスッキリして気合が入るというメカニズムなんだろうけど、それでも僕には十分すぎる。 早いこと出てくる必要がなくなるように、気合い入れてやっていこう。

最期に直接言えなくてちょっと後悔してるけど、ありがとう。お盆には帰るよ。