猫のトイレ回数や時間をIoTボタンを使ってGoogleカレンダーに記録する


この記事は KLab Engineer Advent Calendar 2019 の18日目の記事です。

はじめに

今年の2月から猫を飼い始めました。

出会ったときは手のひらに収まりそうなくらい小さかったですが、今では立派な成猫に。
つい先日、1歳を迎えたばかりの男の子です。

猫って本当にかわいいですね。
世の中にこんなにもかわいい生き物がいるものか、いや、いる!と昂ぶってしまうくらいのかわいさです。

閑話休題

さて、猫は種々の動物たちの例に漏れず、体調が悪くてもそれを隠そうとします。
ですので、普段から様子をよく観察し、ご飯の食いつきが悪くなった、目ヤニが出るようになった、耳や体をよく掻く、といったささいな変化でも確実にキャッチすることが大事です。

その一環として、トイレの回数や時間の記録をつけているのですが、
今回は、その作業をもっと楽にできないかと取り組んでみました。

全体の構成

理想は猫がトイレをしたときに自動で記録されるといいのですが、重量センサーや赤外線センサーを使って判別するのは精度を出すのが難しそうなので、今回はシンプルに
「猫がトイレをしたのを確認したら、IoTボタンを押してGoogleカレンダーにイベントを作成する」
という形にしました。
在宅時にしか使えませんが、最初はこんなものでいいでしょう。

[IoTボタン]->[Raspberry Pi]->[Googleカレンダー]
というシンプルな構成です。

IoTボタン

最初はAmazon Dash Buttonを使おうと考えていたのですが、Dash Buttonの販売は今年の2月末で終了したそうです。
代わりにAWS IoT エンタープライズボタンというものがありますが、価格は2500円とDash Buttonの5倍ほどするので、少し躊躇われます。

そんな中、こちらの記事を読んでダイソーにBluetooth接続できるボタンがあることを知り、今回はこれを使ってみることにしました。
参考:IOTボタン対決 Amazon Dash ボタン VS ダイソーBLE300円ボタン

ダイソーのBluetoothリモートシャッター

100円ショップのダイソーで買うことができます。価格は300円です。Amazon Dash Buttonよりも安価ですね。
技適も取得しているので、技術ブログでも安心して取り上げることができます。

本体は側面の電源スイッチと、iOS用/Android用のボタンがあるだけのシンプルな作りです。
仕組みとしては、Bluetooth接続したスマホにVolumeUpの信号を送ることで、カメラのシャッターを切っているようです。

ラズパイとの接続

参考:わずか300円でIoTボタンを作る方法
こちらの通りにやるだけで、ものの数分でラズパイとリモートシャッターを接続することができました。
ところが………。

イベントが取得できない

bluetoothctlコマンドでペアリングするところまでは何も問題がなかったのですが、その後、bluebuttonコマンドでイベントを取得しようとしてもイベントが飛んできません。

bluebuttonのコードをいじってログを吐かせてみてもやっぱり飛んでこないので、「[Linux] /dev/input からマウスイベントを取得する」を参考に

$ sudo hexdump /dev/input/event0

を確認すると、そもそもここにも何もイベントが来ていません。

bluetoothのon/off、ラズパイの再起動、初期不良を疑ってリモートシャッターをもう1個買ってきて確認してもやはりダメ。
これは困った………。

どう解決したか

半分あきらめかけていたのですが、「100均Bluetoothボタンをラズパイに活用」という記事内でこのような記述を発見

うまくいかない場合
ペアリング&接続は出来ているけど、ボリューム制御ができない場合があります。クラゲも何度かその状態を経験しています。その場合は、切断や削除などを行い、何度かペアリング&接続を試してみて下さい

そんなバカな、と思いつつ、10回ほど接続→切断を繰り返してみると、突然イベントが届くようになりました。そんなバカな………。

Googleカレンダーにイベントを作る

ここまでいけばもう勝ったも同然です。

bluebuttonはボタンを押した/離した際にconfigファイルに記述したコマンドを実行できるので、そこでGoogleCalendarAPIを叩きます。

その前に、まずは猫のトイレを記録する用のカレンダーを作成しておきます。
Googleカレンダー右上の[設定]->[カレンダーを追加]->[新しいカレンダーを作成]で記録用のカレンダーを作成します。
その後、マイカレンダーに作ったカレンダーが追加されるので、カレンダーIDをメモっておきましょう。

イベントの作成

イベントの作成は何でやっても良いのですが、今回は昨年と同じGoでやりました。

公式のリファレンスにほぼそのまま使えるサンプルがあるのでこれを流用します。
認証まわり:Go Quickstart
イベントの作成:Create Events

quickstart.go
t := time.Now().Format(time.RFC3339)
event := &calendar.Event{
  Summary: "猫のトイレ",
  Start: &calendar.EventDateTime{
    DateTime: t,
    TimeZone: "Asia/Tokyo",
  },
  End: &calendar.EventDateTime{
    DateTime: t,
    TimeZone: "Asia/Tokyo",
  },
}

calendarId := "ここにカレンダーIDを記載"
event, err = srv.Events.Insert(calendarId, event).Do()
if err != nil {
  log.Fatalf("Unable to create event. %v\n", err)
}
fmt.Printf("Event created: %s\n", event.HtmlLink)

あとはビルドした実行ファイルをbluebuttonのconfigファイルから呼んでやれば完成です。

bluebutton
keyup=echo UP
keydown=~/quickstart
longup=echo LONG UP
longdown=echo LONG DOWN

リモートシャッターをIoTボタン化して、Googleカレンダーにイベントを登録することができました。

課題

とりあえず最低要件は達成できましたが、いくつか改善すべき点があります。

大と小

ご存知の通り、トイレには大と小があり、それは猫でも同じです。
今回使用したダイソーのBluetoothリモートシャッターにはボタンが2つあり、偶然にも大きいボタン小さいボタンなので直感的に使い分けることが可能なのですが、どちらのボタンを押しても同じ内容のイベントが飛んでくるため、区別することができませんでした。
先述した「100均Bluetoothボタンをラズパイに活用」によると、Android用のボタンはKEY_ENTERが追加で飛んでくるはずなのですが。仕様が変わった?

スリープ状態からの復帰

ダイソーのリモートシャッターは、最後に通信が行われてから90秒経過するとスリープしてペアリングが解除される仕様になっています。
スリープ状態の時にリモートシャッター側のボタンを押すと、数秒で再度ペアリングできるのですが、ボタンイベントを飛ばすためにはペアリング完了後に再びボタンを押さないといけないため、少々面倒です。
keepaliveするなり、ペアリングイベント自体をフックにするなりして解決したいポイントです。

終わりに

当初はあまり期待してなかったのですが、ダイソーのBluetoothリモートシャッターは安価な上に接続も簡単で、ボタンの使い分けやスリープ問題が解決すれば便利なIoTボタンとして自宅や会社などいたるところで使えそうです。

これからも、人と猫との生活のQoLを上げられるよう、色々と便利なモノを作っていきたいです。ねこはいます。