これは はてなエンジニア Advent Calendar 2024 24日目の記事です。
こんにちは、 id:masawada です。30代に突入して早一年、だんだんと健康が気になり始めています。エンジニアたるもの無闇にアクションを取る前にまずは計測、ということでiOSにプリインストールされているヘルスケアアプリの情報をMackerelに送信して傾向を眺められるようにしてみます。
(注意: 株式会社はてなはPHRサービス事業協会に加入している法人ではありません。この記事は健康に関するデータをMackerelに記録することを公式に推奨するものではありません。あくまで個人利用・自己責任の範囲で参考にしてください)
iOSにはショートカットというアプリもプリインストールされています。ショートカットでは「アクション」と呼ばれるブロックを積み重ねることでワークフローを構築することができます。「アクション」にはヘルスケア情報を取得するものや特定のURLにHTTPリクエストを送るものなども用意されているので、これらを組み合わせればMackerelに値を送信できます。
Apple Watchを着用している場合、心拍数や歩数などのメトリックがヘルスケアアプリに自動で記録されます。また、ヘルスケアアプリと連携するサードパーティアプリを利用している場合は、それらに記録されたメトリックも自動で吸い出して集約してくれます。たとえば自分はオムロン製の血圧計、体重計、体温計を利用しており、OMRON Connectというアプリを経由してヘルスケアアプリにメトリックを集約しています。今回は心拍数を送信する例をご紹介します。
ショートカットアプリを実際に触る前に、まずはMackerelにどう値を送信するか確認しておきましょう。今回はサービスメトリックとして情報を保存することにします。
mackerel.io
Mackerelで事前にServiceを作成して、curlで以下のようなリクエストを送ると値を投稿することができます。
curl -H 'Content-Type: application/json' \
-H 'X-Api-Key: <apiKey>' \
'https://api.mackerelio.com/api/v0/services/<serviceName>/tsdb' \
-d '[{ "name": "heart_rate.count", "time": 1734966000, "value": 80 }]'
ヘルスケアのメトリックを取得し、それをJSON形式に加工してこのURLにPOSTするのが今回のゴールです。というわけで、まずはヘルスケアのメトリックを取得します。
ショートカットアプリを開いて新規にショートカットを作成し「ヘルスケアサンプルを検索」アクションを配置しましょう。「クイックルック」アクションを接続することで取得した値を表示することができ、デバッグに便利です。Mackerelのサービスメトリックは最大で過去24時間分の値を記録できるので、検索する範囲は過去1日として心拍数を取得してみます。一旦情報を取得できていることだけを確かめたいので1件だけ取得することにします。
これを実行すると結果が表示されます。
酒を飲んだ後だったので心拍数がやけに高いですね。これだけで不健康さがよく分かります。
値を取得できることは分かったので、これを加工してJSONにしましょう。「各項目を繰り返す」というアクションを利用すると、いわゆるmap的な処理を実現することができます。これを配置して入力の引数を「ヘルスケアサンプル」に指定します。繰り返しの中に「ヘルスケアサンプルの詳細を取得」アクションをふたつ配置し、「繰り返し項目」から「開始日」「値」をそれぞれ取得します。「テキスト」アクションを配置すると、開始日と値を変数として埋め込んだテキストをフォーマットできます。テキストの内容として {"name": "hr.count", "time": 開始日, "value": 値}
を設定することで、JSONを生成します。
この結果も「クイックルック」アクションで見てみましょう。
それっぽいJSONが表示されました。現段階では、mapした結果のitemがひとつずつ表示されています。また、timeの値がepoch秒になっていません。このままではリクエストボディとして利用できないので修正していきます。
まず結果を結合して配列にします。「テキストを結合」アクションを繰り返しの終了の後に配置し、繰り返しの結果をカンマで結合します。さらに、結合してできた文字列を「テキスト」アクションに埋め込んで []
で囲います。
次に時刻をepoch秒に変換します。繰り返し項目から開始日を取得するアクションの次に「端数を処理」アクションを接続します。数値計算系アクションの引数に日付を入れるとepoch秒(小数部5桁の精度)にキャストされる仕組みを利用しています。四捨五入と表示されますが、常に切り捨てるモードを選ぶことができます。後続の「テキスト」アクション内の「開始日」を「端数処理済の数値」に置き換えましょう。これでepoch秒を挿入することができます。
ここまででMackerelに送信するJSONを生成することができました。うまく構成できていれば以下のようになるはずです。見やすいように直近の3件をサンプリングしていますが、実際に送信する際はヘルスケアサンプルを取得するアクションの項目数を制限するチェックを外しています。
ここからは生成した値をMackerelに送信します。HTTPリクエストには「URLの内容を取得」アクションを利用します。このアクションを最後に接続しましょう。サービスメトリック投稿APIはPOSTでリクエストを送るので、方法(HTTPメソッド)はPOSTです。送信するのはJSONですが、「本文を要求」をJSONにすると値を送ることができません。サービスメトリック投稿APIでは、トップレベルが配列形式のJSONがリクエストボディとして要求されています。しかし「URLの内容を取得」アクションでJSONを送信しようとするとトップレベルが辞書形式のリクエストになります。これを回避するため「本文を要求」は「ファイル」に変更し、引数として生成したJSONのテキストを選択します。また、ヘッダとして Content-Type
を application/json
に、 X-Api-Key
をMackerelから取得したAPIキーにそれぞれ設定します。これらを済ませると以下のような形になっているはずです。
試しにこれで実際にどういうリクエストが送られるのか確認してみましょう。nc
コマンド*1でHTTPリクエストを待ち受けて様子を眺めてみます。
$ ( echo "HTTP/1.0 200 OK"; echo; echo "it works!" ) | nc -l -p 8080
POST / HTTP/1.1
Host: 192.168.0.56:8080
Content-Type: application/json
Connection: keep-alive
X-Api-Key: dummy_api_key
Accept: */*
Accept-Language: ja
Content-Length: 172
Accept-Encoding: gzip, deflate
User-Agent: BackgroundShortcutRunner/3218.0.9 CFNetwork/1568.300.101 Darwin/24.2.0
[{ "name": "hr.count", "time": 1734964612, "value": 141 },{ "name": "hr.count", "time": 1734964603, "value": 140 },{ "name": "hr.count", "time": 1734964598, "value": 139 }]
実際にMackerelに送信したい内容と同等のリクエストになっていることが確認できますね。ということで、あとはURLをMackerel APIのものに置き換えれば完成です。実際に投稿すると以下のようなグラフを眺められるようになるかと思います。
心拍の落ち着いているタイミングを見るに、昨日は5時過ぎに寝て10時ちょっと前に起きているのがよくわかります。来年はこういったメトリクスを駆使して健康になれるようなアクションを取っていきたいですね。
明日の担当は id:yujiorama さんです。