テクノロジの無駄づかい

日々の「ステュディオス」を求めて遠回りしがちなエンジニアの記録

Google Homeでリモコン操作をやってみた。その2

http://hero.hatenablog.jp/entry/2018/01/08/094908hero.hatenablog.jp

上に示したポストで Google Home -> Node-RED までを書いたけど、これはその続き。 Node-RED でMQTTのメッセージを受けた後、赤外線を発するまでを書く。

irMagicianの準備

Node-REDのフローのフローを作成する前に、その要素として必要になる赤外線リモコン irMagicianの準備をしておく。

f:id:HeRo:20180108140216p:plain

irMagician は大宮技研製の赤外線リモコンモジュール。 Raspiとの組み合わせでよく使われるものらしく、Amazonでも購入できる。 USB接続なので、電子工作苦手な私にはありがたい。

最初はirmagicianを使ってリモコン信号を記録しようとMacBookに接続して、動かしてみた。 その時点では、少々不安定なものの動いており、それらしいデータも取れていた。

しかし、そのデータを書き込んで動作確認するも、ターゲットのシーリングライトは全く反応しない。 キャプチャしたリモコン信号データが、Macではうまく書き出せないのかなと思い、 Raspi上で試すと今度は書き出したデータを書き込もうとするとエラーが発生する。 こりゃダメだと思い、仕方なくJSのライブラリの利用は諦めた。

ググると、pythonを使っている人が多そうだった。 で、netbuffalo/irmcli: irMagician command line utilityを試してみた。

irmcliのインストール

インストールと言っても irmcli自体は単なる puthon のスクリプトなので、github から cloneするだけ。

ただ、素のraspiだと、git や pip がないのと、pyserial に依存しているのでそれらをインストールする必要がある。 手順は次の通り。

sudo apt-get update
sudo apt-get install git python-pip
sudo pip install pyserial
git clone https://github.com/netbuffalo/irmcli.git

リモコンデータの作成

リモコン信号のキャプチャするには次のコマンドを実行し、リモコン受光部に実際のリモコンの赤外線を浴びせる。

$ python irmcli/irmcli.py -c

キャプチャした信号は irMagician に保持されているので、次のコマンドで、発信することができる。 irMagicianを操作したい機器に向ければそれで操作できる。

$ python irmcli/irmcli.py -p

リモコン信号をファイルに書き出すには次のコマンド。

$ python irmcli/irmcli.py -s -f [書き出し先ファイルパス]

書き出したファイルを読ませてリモコンを発信するには次のコマンドを使う。

$ python irmcli/irmcli.py -p -f [読み込ませるファイルパス]

一通りのやりたい操作をRaspi上で試して問題なく動作したので、 ターゲットのシーリングライトのリモコンのON/OFFの信号をキャプチャしてファイルに保存しておく。 irMagicianが保持できるリモコン信号は1つで、しかも揮発性で電源落とせば消えてしまう。 したがって、onとoffのそれぞれの信号をデータとして保存しておいて、発信するたびに読み込ませる必要があるのだ。

事前準備はここまで。

Node-REDのフローの登録

いよいよNode-RED上のフローを登録する。 次のようなフローにした。

f:id:HeRo:20180108100726p:plain

mqtt in ノードは その1 に書いたとおりの設定。 それ以降の設定を順に説明する。

JSON ノード

文字通り、受けたメッセージをJSONに変換する。 その1で書いたように、IFTTTからBeebotteには次のようなJSONを送信している。

{ "data": {"room": "bedroom","device": "light","action":"{{TextField}}"} }

これが、次の様にmag.payloadstringで入れられて送られて来る。

"{"data":{"room":"bedroom","device":"light","action":"オン"},"ispublic":true,"ts":1515375632470}"

これを後の処理で使いやすいようにJSONをオブジェクトに変換する。

{"data":{"room":"bedroom","device":"light","action":"オフ"},"ispublic":true,"ts":1515375632470}

Change ノード

Changeノードを制すものがNode-REDを制すほど使い勝手のあるノードらしい。

ここでは次の3つの設定で値を書き換えてある。

f:id:HeRo:20180108104648p:plain

1つ目と2つ目は オンオフをそれぞれ、onoffに書き換えている。 3つ目で、msg.payload.datamsg.payloadにセットして必要な値のみ後続の処理に送るようにしている。

Switchノード

Switchノードは条件分岐を行うノード。 ここでは、msg.payload.actionの値 on|offにより、点灯するのか消灯するのかを制御する。 次の様に設定した。

f:id:HeRo:20180108105715p:plain

Exec ノード

前述の様に node様のJSのライブラリはうまく動かなかったので、それを内部で利用する node-red-contrib-irmagician を使うことはできなさそうということで、pythonCLIツールirmcliを Execノードで実行することにした。 まあ、Exec ノードがあればどんな言語で実装されたものでも、コマンドラインで実行できればなんとかなる。

次の様に設定した。 次のキャプチャは 点灯用だけど、同様に消灯用も登録する。違うのは読み込ませるjsonファイルのみ。 f:id:HeRo:20180108110841p:plain

ついにシーリングライトのON/OFF

これですべての設定が完了した。

今回、利用したRaspiは 昔に買って、使い道なく放置していた RASPBERRY PI 1 MODEL B+。 Raspiの中でスペックは最も低いが、代わりに消費電力はPi3より小さいし、Node-REDを動かすだけなら問題ない。 ただ、起動して動作するまでは少々時間がかかるが、ずっと稼働させておくものなので気にならない。

f:id:HeRo:20180108141028p:plain

実際にやってみると、irMagicianの赤外線が方向にシビアできちんと対象の方向に向けていなければ赤外線が届かない。 それで、irMagicianを繋いでいるUSBケーブルに針金を巻き付け方向を保っている。

Google Homeに「OK Google、寝室ライトオン」と話しかければシーリングライトが点灯するし、 「OK Google、寝室ライトオフ」と話しかければ消灯する。 これまで、夜明かりをつけるときには暗闇でリモコンを探していたので思った以上に便利だ。

本当は、寝室のエアコンも操作したかったところだが、irMagicianの指向性が思った以上に強く、一つでいろんな家電を操作するには使いづらい。 LED拡散キャップも試してみたが、今度が距離的に厳しくなりすぎる。 ラズベリー・パイ専用 学習リモコン基板 だと部屋に1個で事足りるようになるだろうか。

まあ、しばらくは寝室の明かりだけかな。

あ、部屋を片付けてたら、大昔に買った PC-OP-RS1が出てきた。これまだ動くかなぁ。

Google Homeでリモコン操作をやってみた。その1

Amazon Echoの招待がなかなか届かないので*1Google Home を買ってみた。 使ってみて、ちょっとしたことを声だけで操作できるのは思った以上に便利だと感じる今日このごろ。

音楽を流したり、ちょっとした質問に答えてくれるのも便利だし、なぞなぞとか早口言葉をやってくれるのも面白いのだが、 やはり家電機器の操作もやってみたい。

とりあえず、最初は定番、電灯のON/OFF。我が家のシーリングライトは赤外線リモコン操作なので、リモコン信号を操作できれば実現できるだろう。

最初から連携するデバイスを購入してしまえばもっと簡単なのかもしれない。 しかしデバイスメーカーのサーバサイドサービスに依存するとサービスが終了した場合に機器ごと使えなくなってしまいかねないので、なるべく自作できるものの組み合わせで実現したい。

で、以下の組み合わせでやってみた。

うーん、どうもやりたいことのシンプルさの割に実現する仕掛けが複雑すぎるように思う。 しかし、2018年正月の時点ではインターネットに晒す自前サーバの運用なし構成ではこれでもシンプルな方かと思う。

以下にそれぞれの連携方法について書く。

Google Assistant -> IFTTT -> beebotte

IFTTTにGoogle Assistant のトリガーが用意されている。 そのトリガーとWebhookを利用して beebotte にメッセージを送る。 ここまでで、Google Assistantへの音声入力をプログラムから扱える形のメッセージに変換することになる。 そこまでを順に説明する。

Beebotte の設定

IFTTTのアプレット登録に、REST APIのエンドポイントが必要になるので先にBeebotteの設定を行う。

Beebotte はIoT向けのクラウドサービス。REST API や MQTTをサポートして、接続された機器にリアルタイムにメッセージを通知することができる。今回はこのサービスをMQTTブローカーとして利用する1。 50,000メッセージ/日までならフリープランで利用できる。個人ユースにはそれで十分であろう。

まずはアカウントを登録する。フリープランならユーザ名、メアドとパスワードだけで登録できる。 アカウントを作成したら、チャネルとリソースを登録するよう促される。 特に迷うところはないと思う。 登録したらこんな感じ。

f:id:HeRo:20180103182319p:plain

Channel名とresource名で REST API や MQTTのエンドポイントが決まる。 また Channel Token が発行されるが、これは後にIFTTTのアプレット登録やNode-REDから接続する場合に必要となる。

IFTTTでのアプレット登録

Google Assistant トリガーは4種類ある。用途に応じて使えば良いが、今回は Say a phrase with a text ingredient を利用する。これは音声入力したテキストをアウトプットに渡せる。今回は電灯の操作なので、オンオフを渡して制御する。

入力項目は次の通り。

設定項目 設定値
What do you want to say? 寝室ライト $
What's another way to say it? (オプションなのでよしなに)
And another way? (これもオプションなのでよしなに)
What do you want the Assistant to say in response? 寝室ライト $ しました。
Language Japanese を選択

What do you want to say? の設定が肝となる。 寝室ライト $と設定したが、「寝室ライト」の部分が 「OK Google,」の後に続くコマンドワードで何をしたいのかを示すキーワードとなる。 「$」はコマンドワードのパラメータとして設定できる音声入力のプレースホルダーで、ここでは「オン」あるいは「オフ」を想定している。 つまり、「OK Google, 寝室ライト オン」とGoogle Homeに話しかければ、このアプレットが起動して、$=オン がセットされるということとなる。

What do you want the Assistant to say in response? は音声コマンドを復唱させることにより、入力が正しいかどうか確認するために設定する。

続いて、アプレットの後半、アウトプットの設定。 このアプレットの目的は beebotte にメッセージをパブリッシュすること。パブリッシュの手段としてはbeebotteのREST APIを利用する。 そのため、アウトプットには Webhookを利用する。

設定は次の通り。

設定項目 設定値
URL http://api.beebotte.com/v1/data/publish/<Channel名>/<resource名>?token=
Method POST を選択
Content Type application/json を選択
Body {"data": [{"room": "bedroom","device": "light","action":"{{TextField}}"}]}

URL にはbeebotteのメッセージパブリッシュのRESR APIを指定する。先に登録した チャネルとリソースをパスにはめこんで、認証のためクエリパラメータtokenChennel token を設定する。 前述の スクリーンキャプチャに合わせると http://api.beebotte.com/v1/data/publish/MyIoT/light_at_bedroomというのがAPIのURLとなる。

Body には、APIに送信するデータを設定する。ここにはJSON形式で、後にNode-REDで処理する時に必要になりそうなパラメータを送信できるようにする。{{TextField}}には $ にセットされたテキスト、すなわち「オン」または「オフ」が入ることになる。

beebotte に Node−REDで接続

前節までで、Google Homeへの音声コマンドが、beebotte 上のメッセージに変換されるところまでができている。 あとは、Node-REDでそのメッセージを受け、処理するだけ。

Raspberry Pi やその上で動くNode-REDの準備は次に書いてあるとおり。

Node-REDではデフォルトで MQTTがサポートされている。 beebotte と連携するには mqtt in ノードを利用する。

mqtt in ノードの設定はこんな感じ。 トピックには [Channel名]/[resource名]を設定する

f:id:HeRo:20180107191427p:plain

mqtt in ノードのサーバ設定は別フォームが開くので次のように設定する。 セキュリティタブで、ユーザ名に token:[Channel Tokem] を設定するのが肝。

f:id:HeRo:20180107190740p:plain

設定項目 設定値
サーバ mqtt.beebotte.com
ポート 8883
SSL/TLS接続を使用 チェックする。TLSの設定は不要
ユーザ名 token:[Channel Tokem]

設定が正しく、beebotte に接続できる場合にはフローのデプロイ後、次のようにノードに 接続済 と表示される。

f:id:HeRo:20180107192117p:plain

動作確認

ここまでの作業を確認すために次のようなストリームを作って、Google Home => Node-REDに届くメッセージを確認する。

f:id:HeRo:20180108000451p:plain

OK Google, 寝室ライト オン」とGoogle Homeに話しかけて、 Debugノードのデバッグ出力に送ったメッセージが出力されれば成功。

さて、

この先は Node−REDでフローを作って、赤外線を発するまでだけど、ここまでで結構長くなったので、続きはまたあとで。

その2に続く。

hero.hatenablog.jp


  1. IFTTTはまあ大丈夫だろうけど、デバイスメーカーのディスコンを嫌う割にbeebotteなどというイマイチ聞いたことないサービスを使って大丈夫か?とも思ったが、いざとなればHerokuでCloudMQTTを動かすという代替策があるので採用した。ただし、CloudMQTTは無料では同時接続デバイスが10と少々少ない。

*1:今はやっと手元に届いたところ。

Severlessをやってみた

AWSでちょっとした処理を実行するのに Lambdaはとても便利。

これまで、ちょっとしたことをLambdaで実装したことはあったのだが、 ちょっとしたこと以上のコードをAWSコンソール上で書くのはなかなかつらい。

そこで、Serverless Frameworkを使ってみようと思った。

当初、Serverlessを使うメリットとして考えていたのは次のとおり。

  • ローカルの使い慣れたエディタでLambdaを実装できる。
  • Webpack + Babel プラグインを使えば、モダンなJavascriptの機能を使える。
  • デプロイも楽になりそう

試しに簡単なアプリケーションを書いてみた。

サンプルアプリのシナリオ

試してみる課題シナリオは次のとおりとした。

  1. Githubにプルリクを作ったり、プルリクにプッシュするとCodeBuildでビルドを実行する。
  2. プルリクには Github の Status APIで状態を通知する
  3. ビルドの開始をSlackにも通知する
  4. CodeBuild の処理終了をCloudWatch Eventで受け、その結果をGithub の Status APIで更新する
  5. 結果をSlackにも通知する。

作ってみたコードはこちら => HeRoMo/ServerlessSample: My First Serverless Application Sample

やってみて

やってみてどうだったのか?感想を以下に述べる。

関連リソースも一緒に定義可能

予備知識少なめで取り掛かったのでドキュメントを読みながら進めた。 最初はトリガーとなるAmazon SNS等は別に登録して用意しないといけないのかな?と思っていたのだが、 SNSを始め、API Gatewayなど、Lambdaをトリガーできるリソースは serverless.ymlの関数定義で一緒に定義できる。 これはとても便利。 また、Lambdaから他のAWSのリソース・サービスにアクセスするのに必要な権限もserverless.ymlでLambdaの実行ロールに追加できる。 とにかく、Lambdaの実行に必要なものがserverless.ymlで一元管理できるのが管理しやすくてよかった。 最初にデプロイした時、裏でCloud Formationのスタックが作成、実行されてちょっと驚いたが、こういうことなら納得。 Cloud Formationの定義記法で、Lambdaとは直接やり取りしない設定も定義できそうなのでCloud Formationも覚えねばと思った。

やはり async/await は便利

Webpack プラグインとbabelを追加して、ES2015 からNode6.10相当にトランスパイルするように設定した。 Lambdaを使う動機としてはAWSのサービス間の連携を開発するためのグルーコードとして便利というのもあると思う。 実際、AWSSDKは組み込みで動作するので適切な権限がLambdaの実行ロールに付与されていれば、別サービスにアクセスするのは それほど難しくない。 しかし、AWSの実態はHTTPベースのAPIなので、呼び出しはどうしても非同期となる。非同期を多用する必要がある場合には もう、async/awaitが使えないと辛い体になってしまっているので、これは助かった。 AWS SDKの関数は現状、コールバック式なので、早くPromiseを返すようになればと思う。

全般的な感想

前述したメリットはすべて享受できたと思う。 私はJSを書くときにはAtomエディタを使うことが多いが、やはり使い慣れたエディタだと実装しやすい。 webpackでビルドできると、babelでトラスアイルできるだけでなく、自由にnodeモジュールを使うこともできるので、 Github APIやSlack APIのライブラリを利用することができ、その点でも効率的に実装することができた。 デプロイもsls deployワン・コマンドで済むのはとても楽だった。

テンプレートも作った

今後別のアプリケーションを実装するのに便利なように、webpack+babelを設定したアプリケーションの雛形を作った。 ServerlessにはServerlessアプリをgithubから取ってきてそれをひな形に別のアプリを作成する機能が備わっている。 そこもよく考えられているなぁと感心した。

テンプレートはこちら。=> HeRoMo/sls-template: Serverless Framework application template

サイトをチェックするツールを作ってみた

ボランティアで防災・減災関連のwebサイトの情報を収集しているが、そのようなサイトは災害時にわっと作られるが、その後、閉鎖されたり、メンテナンスされなくなってしまうサイトも少なくない。同様な地方自治体のホームページも少なからずある。

時々そのようなサイトの状況をチェックするのに便利なツールがなかったので自分で作ることにした。

最初はHTTPでアクセスしてレスポンスをチェックするだけと思っていたのでcurl等を使ってちょっとしたスクリプトで済ませようと思った。だた実際に確認してみると個人の作ったWeb サイトは思ったより続けられていないことが分かった。 同じドメインが全く違うサイトに変わっていたり、閉鎖されてNot Found ページが返ってくるのに レスポンスコードは200だったり。ドメインも管理者も変わってなさそうだけど、内容が全く変わっているものもあった。

また、文章ではなく画像に情報が書いてあったり、地図等の外部コンテンツを埋め込んでいるサイトも多く、レスポンスのテキストにキーワードが含まれるかどうかだけではどうも判別が難しい。ということで、チェックは目視ということになるが、いちいちURLにアクセスしていチェックしなくても良いようにスクリーンショットをとる機能を実装することにした。

その他、必要な機能を検討し、以下のようなツールを作ることとした。

  • URLが有効かどうか実際にアクセスして確認する
  • その際、リダイレクトにも追従できる
  • レスポンスのステータスコードが200ならスクリーンショットを取る
  • 単一のURLでなく、URLのリストを読ませて複数のサイトを一度にチェックできる
  • 結果を再利用しやすい形で出力する

最初は Phantom.js を使って作った

HTTPのリクエスト/レスポンスだけなら色々方法はあるが、スクリーンショットを取るとなると使えるツールが限られてくる。 実行中にブラウザがぱたぱた動くのも鬱陶しいし、できればGUIを持たないサーバで動かせるようにしたい。 となると、Phantom.js を使うのが最も手軽かと思い、最初のプロトタイプを作った。 実際には、Phantom.js を使いやすくラップしてくれる CasperJS を利用した。

最低限、動くものを作って、さあ、使いやすくブラッシュアップしていこうかなと思った矢先、Chrome 59にて ヘッドレスブラウジング がサポートされ、それに伴い、Phantomのメンテナがやる気を失ったらしい(参考:Phantom.jsのメンテナー、プロジェクトの将来に疑問を呈し、その座を降りる

ということで、PhantomJSをインストールしなくても使えるような実装のほうが良いし、いまやシェアトップとなった Chromeを使うほうが、スクリーンショットの結果も普段使っているブラウザとを乖離せず良かろうと、この実装は捨てることにした。

ヘッドレスChromeへの変更

Chromeをプログラムから操るには、DevToolのリモートプロトコルを使えば良いようで、そのためのライブラリchrome-remote-interface を利用して実装を変更した。 このライブラリ、Chrome DevTools Protocolの機能をほぼほぼカバーしているけれど少々プリミティブ過ぎて、使いづらかった。

例えば、通信が一つ一つハンドリングできるので、ページのレスポンスコードを取るのに、ページからリンクされる画像やCSSその他へのリクエストの中からドキュメントへのリクエストを探して得る必要があるとか、それもリクエストIDが取れるのでそのIDで再度結果を取得する必要があるとか、とにかく煩雑で、やりたいことに対して実装しないといけないことが多かった。おまけに、Chromeそのものを起動する機能は持っていないので、GoogleChrome/lighthouseを使ってChromeの起動を実装する必要があった。

このまま実装を続けても、この先の機能追加とか辛そうと思っていた矢先、GoogleChrome/puppeteer を知った。

puppeteer に切り替え

puppeteer だと、次のようにほんの数行書くだけで、Chromeをヘッドレスで起動して、URLにアクセスしてスクリーンショットを撮ってブラウザを終了してくれる。

// puppeteer でスクリーンショットのサンプル
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  const response = await page.goto('https://example.com');
  await page.screenshot({path: 'example.png'});
  console.log(response.status)

  browser.close();
})();

同じことを chrome-remote-interface を利用すると次のようになる。スクリーンショットの保存も自前で、しかもこれにはChromeの起動/終了が含まれていない。

// chrome-remote-interfaceでのスクリーンショットのサンプル。
// Chromeの起動/終了は別途必要。
const CDP = require('chrome-remote-interface');
const fs = require('fs');

CDP(async (client) => {
    const {Page} = client;
    try {
        await Page.enable();
        await Page.navigate({url: 'https://example.com'});
        await Page.loadEventFired();
        const {data} = await Page.captureScreenshot();
        fs.writeFileSync('scrot.png', Buffer.from(data, 'base64'));
    } catch (err) {
        console.error(err);
    } finally {
        await client.close();
    }
}).on('error', (err) => {
    console.error(err);
});

また、puppeteer だと、レスポンスコードも簡単に取れるが、chrome-remote-interface だと更に Networkクラスを使って、Page.navigateのレスポンスコードを探して取り出す必要がある。

これだけ比べても、puppeteer に乗り換えるメリットを感じたし、Chromeチーム謹製というのも安心感があった。 それで、再度実装を変更した。

site-checker として公開

で、作ったツールを この度、公開した。

我ながら、センスのないベタなネーミングに嫌気がさすが、これというのが思いつかなかったし、npmライブラリとしても使われていない名前だったので、この名前にした。

次のコマンドでインストールできる。

$ npm install -g site-checker

インストールしたら、次のコマンドで、スクリーンショットが取れる。

$ site-checker -u http://hero.hatenablog.jp

できることは次の通り。

特に、フルページとエミュレーションは puppeteer に切り替えてとても簡単に実装できた。

詳しい使い方はこちらを参照 => 使い方 · HeRoMo/site-checker Wiki

災害に関わる「言い伝え」をマッピングしてみた

今日は大晦日。2016年も残りあと数時間。

振り返ると熊本地震、北海道の台風に鳥取地震、そして先日の糸魚川の大規模火災に、茨城での震度6弱と大きな災害が多かった一年だったように思う。

起こってしまった災害をなかったことにはできないが、そこから教訓や今後発生する災害の被害を減らすための知恵を得ることはできるはずと思う。

そのようなことは私でなくても考えていて、公開から少し時間が経ってしまっているようだが、消防庁が収集・整理した防災に関わる「言い伝え」 なる資料が 全国災害伝承情報:総務省消防庁 に公開されている。

そこには資料に添えて次のように記されている。

有史以来、全国で発生した災害は各地に多大な被害をもたらし、それらの災害の教訓は各地域において記録としてあるもの、図画として残されているもの、あるいは物語、ことわざとして伝承されているものなどがあります。  そのような災害にまつわる資料や情報は、これまで国として整理されず今日にいたっており、その多くが各地域に埋もれたままとなっています。  全国災害伝承情報は、そうした各地域に残る貴重な資料を、国として整理集約し、インターネットを活用し広く一般に公開することを目的としたもので、平成16年度から平成18年度にかけて都道府県や市町村などの協力をいただき、調査を通じて収集した情報を整理集約しました。  この情報を通じて、身近な地域に残されている災害に対する教訓を個々人に認識していただき、防災意識高揚に役立てていただくとともに、防災教育用の教材としての活用が図られることを期待しています。

防災に関わる「言い伝え」

防災に関わる「言い伝え」はなかなか興味深い資料で、全国の災害・防災に関する800弱もの言い伝えが一覧でまとめられている。 各言い伝えごとにそれが伝わる自治体名が記されているので地図上にマッピングしてみた。なお、統廃合により自治体が消滅しているものは引き継がれた現在の自治体にマッピングして旧名を併記している。

地図上の色の付いたエリアをクリックすると、その土地に伝わる言い伝えを右ペインに表示する。

防災に関わる「言い伝え」MAP

f:id:HeRo:20161231164740p:plain

地図化したデータを見てみると北海道、東北の日本海側と近畿地方で言い伝えが少ないのがわかる。これらの地域ではもともと言い伝えが少なかったのだろうかそれとも、伝承が途絶え収集できなかったのだろうか。近畿は長らく都があり、大勢暮らしていたはずなのに、どうして少ないのだろうか。 逆に伝承の多い地域は、古くから多くの災害に見舞われてきた結果、地域の知恵として言い伝えが受け継がれてきたということなのであろう。

個々の地域を見ていくと、言い伝えの内容からどのような災害に苦しんできたのかがわかる。水害に関する言い伝えが多い地域は水害に、地震に関する言い伝えが多ければ地震に苦しめられることが多かったのであろう。

栃木県には地名に関する言い伝えが多い。古い地名を見るとその土地でよく起こった災害がわかるようだ。災害の記憶をいまに伝える日本全国「あぶない地名」(週刊現代) | 現代ビジネス | 講談社(1/6)にも同じような記事がある。この記事にあるように安易に地名を変えてしまうのは良くないのではと思ってしまう。

言い伝えの類似性に目を向けると「地震が来たら竹やぶに逃げろ」に類する言い伝えは、全国的に多いのだなぁと気が付く。竹の根が地盤を強固にし地割れを防ぐことができると知られていたのであろう。

また、次のような言い伝えもある。

  • 地震のとき「マンダラッコ、マンダラッコ」と唱えるとよい。(神奈川県平塚市)
  • 地震のとき「マンザイロク、マンザイロク」といって、竹やぶに逃げる。(新潟県新潟市
  • 地震のときは「まんぜえろく」と唱える。(埼玉県毛呂山町

一説によると「まんぜいろく」とは「万歳禄」と書き、末永く神の恩恵を受けられますように、という祈願らしい。 同じような言葉が地域を超えて言い伝えられているのはなぜだろうか? 一見、つながりなさそうな地域で同じような言葉が言い伝えられているのも興味深い。昔は互いに交流があったのだろうか?

雑雑とした印象・感想を綴ったが、眺めていると色々発見がありそうな資料だ。

tingbot アプリを公開してみた

f:id:HeRo:20161011225300p:plain

この所、ちょっと前に届いた、tingbotで遊んでいる。
ひととおり機能を試したので、アプリを作ってみた。

作ったのは天気予報を表示するアプリ。
どこかのAPIから天気予報を取ってきて、それを表示するだけ。 出かける前に傘が必要かどうかが分かる程度でも便利かなぁと。

サクッと実装

とりあえず、情報源として天気予報APIを探さねばとぐぐってみると OpenWeatherMap が見つかった。 3時間単位で予報が取れ、詳細なのだが、海外のサービスなのでデータはすべて英語。 また、APIを利用するためにはアカウントを作って、API Keyを取らないといけないのがちょっと面倒。 アプリをダウンロードした人にもこの面倒をお願いすることになる。 更に天気予報を取得する都市コードを探すのもちょっと面倒そう。

無料で、使用制限が緩くて、登録など不要で使えそうなAPIで、できれば日本語のAPIということで探してみると、 Weather Hacks - livedoor 天気情報 くらいしか なさそうだった。
降水確率が含まれないのは残念だが、手軽さを優先して今回はこれを採用。

Python でコードを書くのは久々なので思い出しながら、作ってみた。

見栄えを変更

最初のデザインはこんな感じ。いたってシンプルな表示。

f:id:HeRo:20161011231152p:plain

Weather Hacks - livedoor 天気情報APIでは アイコンのURLも含まれているので、画面に表示しているのはそのアイコン。 でも、画像サイズが小さく、これ以上拡大して表示すると粗さが目立ってしまう。 文字を読まなくても絵を見てわかるほうが、離れたところからも確認できるだろうと 思ったので、もっと大きく、わかりやすく天気を絵で表示できるようにすることにした。

ライセンスフリーのアイコン集とかを探してみると、天気アイコンをフォントとして公開しているものもあることに気がついた。 フォントのほうが、大きさや色を自由に調整できるのはと思い、天気フォントを探すことにした。

良さげなのは次の2つ。

前者は各天気がひとつづず1文字に割り当てられたもの。 後者は天気要素をばらして文字にしており、必要に合わせて組み合わせて使うもの。

後者のForecast Font は太陽はオレンジ、雲はグレーと塗り分けられ、それらを組み合わせて ひとつのアイコンを作れそうなのが良いなぁと思い、採用。

次はフォントを組み合わせて各天気に対応したアイコンを作っていく作業。 やってみて思ったのは、外国には「晴時々雨」とか「雨のち曇」っていう表現はないのかな?ってこと。 もともとのAPIで取れる天気予報には例えば「雨時々晴」という予報があり、それに対応した天気アイコンが用意されているが、 いろいろ見た天気フォントにはそのようなどっち付かずの状態を表すようなものがなさそうだった。
このような表現は日本特有なのかな。 などと思いながら、少々面倒だったが、APIが返す30種類の天気それぞれにフォントの組み合わせを実装した。

そして公開

さて、ひととおりできたので、公開してみようとマニュアルで方法を確認する。 tingbotでは The Tingbot Oceanというサイトで、tingbot アプリを募って公開している。

ここに自作アプリを公開する手順は次の通り。

  1. 楽しいor便利なアプリを作る
  2. アプリのソースにicon.png と言う名前でアイコンを追加する(なくてもアプリを実行することはできる)
  3. 更にapp.tbinfoという名前でアプリ名や作者、Webサイトなどのメタ情報をつける
  4. ソースをGithubにpushする
  5. The Tingbot Oceanリポジトリにある ocean/apps.yamlに公開したいアプリの情報を追記して、プルリクを投げる
  6. マージされるのを待つ

で、マージされれば、The Tingbot Ocean に次の様に公開される。 登録の仕方が、なんとも手作りな感じだが、PRして中一日くらいでマージされた。

f:id:HeRo:20161012010437p:plain

日本語を表示するアプリでは最初の公開かな。

Tingbotを少し試してみた。

前回は、組み立ててサンプルアプリを動かしてみただけだったが、もう少しいじってみた。

↓前回↓ hero.hatenablog.jp

イメージファイルの表示

アニメーションGIFはサンプルアプリで試せるので、 それ以外でどんな画像フォーマットが表示できるのか試してみた。

試した結果は次の通り

フォーマット 表示結果 備考
GIF アニメーションもOK
PNG アルファチャンネル付きもOK
JPG
MBP
WebP エミュレータでは利用できず

それを表示した画面写真は次の通り。 右下方は 33.3%の 赤、青、緑 の半透明PNGを重ねている。

よく使われる画像フォーマットは問題なく表示できる。
WebPは実機では問題ないが、エミュレータではエラーとなり起動もできなかった。

日本語の表示

日本語を表示できるのか?

これは日本人にとっては重要なのだが、英語圏の人々からは軽視されている機能なので試してみた。

結果としては、表示可能。

ただし、次の3項目に注意する必要あり。

  1. # coding: utf-8ソースコードエンコーディングを指定すること。
    さもないとコード内に日本語を含むとaccii文字として解釈されてエラーとなる。
  2. unicode文字列を使うこと。
    リテラルでは u'ほげ' を使い、ネットワーク越しに日本語を取得したときにはunicode()関数や.decode()メソッドでunicode文字列に変換する
  3. フォントをアプリに同梱して、コード内で指定すること
    フォントファイルのファイル名にハイフン-を含んでいるとエラーとなるので注意。

アプリを公開する場合、同梱するフォントは再配布可能なライセンスである必要があるので注意。
フォントファイルのフォーマットは TTF でも OTF でもOK。

サンプルコードはこんな感じ。

# -*- coding: utf-8 -*-

import tingbot
from tingbot import *

# setup code here

@every(seconds=1.0/30)
def loop():
    # drawing code here
    screen.fill(color='black')
    message = u'日本語もOK'
    screen.text(message, xy=(0,0), color="white", align='topleft',
                font='fonts/NotoSansCJKjpRegular.otf')
    screen.text(message, xy=(0,45), color="blue", align='topleft',
                font='fonts/ipagp.ttf')
    screen.text(message, xy=(0,80), color="aqua", align='topleft' , 
                font='fonts/ipamp.ttf')
    screen.text(message, xy=(0,120), color="lime", align='topleft' ,
                font='fonts/cinecaption226.ttf')
    screen.text(message, xy=(0,160), color="yellow", align='topleft', 
                font='fonts/TanukiMagic.ttf')
    screen.text(message, xy=(0,200), color="fuchsia", align='topleft', 
                font='fonts/kiloji_p.ttf')

tingbot.run()

これを表示した結果は次の通り。

Webhook

この機能は使い方によっては面白いことができそう。 URLを指定して、ネットワーク上の画像を表示したり、APIにアクセスしてデータを取ってくることもできるのだが、 Webhookはネットワーク越しにTingbotにデータを渡すための機能。

例えば、次のようなコードを Tingbotで動かしながら、

# -*- coding: utf-8 -*-
import tingbot
from tingbot import *

font = 'NotoSansCJKjpRegular.otf'
font_color = 'white'
wh_name = 'my_webhook'

screen.fill(color='black')
screen.text('Waiting...')

@webhook(wh_name)
def on_webhook(data):
    data = data.decode('utf-8')
    screen.fill(color='black')
    screen.text(data, color=font_color, font=font)
tingbot.run()

次のコマンドを実行すると、

$curl -X POST -d '日本語!!!' "http://webhook.tingbot.com/my_webhook"

データとして送った文字列を表示させることができる。

マニュアルにも書かれているが、IFTTTのMakerチャネルを使えば、様々なWebサービスをトリガーにして Tingbot にデータを渡して表示させることができるだろう。

ただし、使い方には注意が必要で、Tingbot にデータを渡すには次のようなURLに 文字列をPOSTしてやればよいのだが、 webhook名は予めどこかに登録しておく必要もなく、自由に設定できる。

http://webhook.tingbot.com/<webhook名>

ということはたまたま、Tingbotユーザの誰かと 使っているwebhook名が一致してしまうと、見知らぬ誰かにデータを見られてしまう可能性や見たくもないデータを見てしまう可能性がある。
ある程度、ランダムで長いwebhook名を使えば、当てられることも少ないと思うが、一度送ったデータは次のデータを送るまでずっとTingbotで取れてしまうので、あんまりプライベートなデータを送らないほうが良いだろう。
逆に大勢にデータを配信するのに使えるかと思ったが、他人も同じwebhook名にデータを送信できるので容易になり済ませてしまう。 そういう使い方もしないほうが良いと思う。
おおらかなパブリック性をもったPub/Sub(誰でも投稿できて、誰でも見たい人に配信される)と捉えれば、面白い使い道があるかも。

このエントリーで書いた3つのアプリは次のリポジトリで公開している。 tingbot_samples