iPhone のヘルスケアをショートカットで Cloud Run に送る:400 が続いたので、受け口の側で受け取れる形を広げた

iPhone のヘルスケアをショートカットで Cloud Run に送る:400 が続いたので、受け口の側で受け取れる形を広げた

iPhone のヘルスケアの体重と体脂肪率を、ショートカットで毎朝 Cloud Run の受け口に送り、BigQuery に入れています(全体の構成はGarmin・Plaud・iPhone のライフログを BigQuery に自動で集めた:Cloud Run と手元の Mac を使い分けた設計)。この記事では、ショートカットの設定と、つまずいたところを紹介しようかと思います。

  • 送るのは「最新の1件」だけです。 受け口が同じキーで BigQuery に MERGE するので、送り直しても重複しません
  • つまずいたのは、ほとんどがショートカット側でした。 ショートカットを直すより、受け口の側で受け取れる形を広げたほうが楽でした
  • Cloud Run では、/healthz が 404 になる、作る順番で失敗する、の2つでつまずきました
目次

ショートカットの設定

トークンは長いので、Mac でコピーしてユニバーサルクリップボードで iPhone に貼りました。

アクションを上から3つ並べます(名前は「lifelog ヘルスケア送信」)。

  1. ヘルスケアサンプルを検索
  2. 種類:体重(Weight)、単位:kg
  3. 開始日:過去 3650 日以内(実質、全期間)
  4. 並び順:開始日、新しい順。制限:1項目
  5. ヘルスケアサンプルを検索(2つ目)
  6. 種類:体脂肪率(Body Fat Percentage)、単位:%。ほかは 1 と同じ
  7. URL の内容を取得
  8. URL:<受け口の URL>/v1/health(変数ではなく文字で入力)
  9. 方法:POST
  10. ヘッダ:Authorization に Bearer <トークン>
  11. 本文を要求:JSON で、次の3つのフィールドを足す
ショートカットの「ヘルスケアサンプルを検索」アクション。種類が Weight、開始日が過去3650日以内、単位 kg、グループ分けなし、並び順は開始日の新しい順、制限オンで1項目を取得する設定
ショートカットの「ヘルスケアサンプルを検索」アクション。種類が Weight、開始日が過去3650日以内、単位 kg、グループ分けなし、並び順は開始日の新しい順、制限オンで1項目を取得する設定
キー 値
weight_body_mass 1 の「ヘルスケアサンプル」
body_fat_percentage 2 の「ヘルスケアサンプル」
date 「ヘルスケアサンプル」の「開始日」
ショートカットの「URL の内容を取得」アクション。URL とトークンは塗りつぶしてある。方法は POST、ヘッダに Authorization: Bearer、本文の JSON に weight_body_mass と body_fat_percentage のヘルスケアサンプル、date の開始日の3つのフィールド
ショートカットの「URL の内容を取得」アクション。URL とトークンは塗りつぶしてある。方法は POST、ヘッダに Authorization: Bearer、本文の JSON に weight_body_mass と body_fat_percentage のヘルスケアサンプル、date の開始日の3つのフィールド

フォームにキーと変数を並べるだけなので、JSON を手で書く必要はありません。1つ目と2つ目の「ヘルスケアサンプル」は名前が同じなので、そのアクションの直下に出ているものを選びます。

▶ で実行して {"rows":2} と出れば成功です。初回は、ヘルスケアの読み取りの許可を聞かれます。

毎朝動かすには、オートメーション →「+」→「時刻」(私は毎日 9:00)→「lifelog ヘルスケア送信」を選び、「すぐに実行」にします。確認ありのままだと、毎朝通知をタップしないと送られません。

ショートカットのオートメーションの設定画面。オートメーションは「すぐに実行」、実行時に通知はオフ、いつは「毎日、9:00 に」、行うは「lifelog ヘルスケア送信」
ショートカットのオートメーションの設定画面。オートメーションは「すぐに実行」、実行時に通知はオフ、いつは「毎日、9:00 に」、行うは「lifelog ヘルスケア送信」

「今日の分」ではなく「最新の1件」を送る

「今日の分を送る」と「最新の1件を毎日送る」の比較図。今日の分を送る形では「今日、体重を測った?」の分岐が必要になる。最新の1件を毎日送る形では、月・火・水と毎日送っても、同じ行は上書きされて1行のまま
「今日の分を送る」と「最新の1件を毎日送る」の比較図。今日の分を送る形では「今日、体重を測った?」の分岐が必要になる。最新の1件を毎日送る形では、月・火・水と毎日送っても、同じ行は上書きされて1行のまま

「今日の分」を探すと、測らなかった日に何も見つからず、ショートカットに「なければ送らない」の分岐が要ります。「最新の1件」なら、毎日同じ動きで済みます。

受け口は BigQuery に MERGE(同じキーの行は上書き、なければ追加)で書き込んでいて、キーは(指標名, 計測時刻)です。測らなかった日は前回と同じサンプルが届きますが、同じ行が上書きされるだけです。

受け取った JSON は、BigQuery に書く前に GCS に置いています。書き込みに失敗しても、元の JSON から入れ直せます。

この方法では、測定の履歴をすべて集めることはできません。1日に何回か測っても、送られるのは最後の1件だけです。毎朝の最新の値を見る用途に割り切っています。

つまずいたところ

起きたこと 原因 どう直したか
何度も 400 が返る ショートカットの中で JSON の文字列を手で組み立てていた 平らな形を受け付け、フォームだけで組めるようにした
日付が読めず 400 「日付をフォーマット」の出力が、端末の設定で変わる GMT+9・タイムゾーンなし・スラッシュ区切りも読む
値が 65.0 kg のように来る サンプルを、そのまま文字として入れていた 先頭の数字を読む
URL に変換できないというエラー URL 欄を変数のチップにしていた 文字として直接入力する
体重と体脂肪率が入れ替わる 同じ名前の「ヘルスケアサンプル」が2つある 各アクションの直下に出るものを選ぶ
動作確認のパスが 404 /healthz が Cloud Run の予約されたパスに当たったと思われる /status に変える
Cloud Run の作成が失敗 Secret にトークンを入れる前に Service を作った Secret の箱 → トークン → Service の順にする

ショートカットを直すのをやめて、受け取る側を広げた

受け取る側を広げた図。「+0900」「GMT+9」「スラッシュ区切り」の日付や「65.0 kg」のような単位付きの値を、受け口が日本時間の日時と数値だけにそろえる。iPhone 側は直さない
受け取る側を広げた図。「+0900」「GMT+9」「スラッシュ区切り」の日付や「65.0 kg」のような単位付きの値を、受け口が日本時間の日時と数値だけにそろえる。iPhone 側は直さない

最初は、ショートカットの中で JSON の文字列を組み立てていました。iPhone のキーボードで { や " を打つのはかなり辛く、" を消しすぎたり、値に単位が付いたまま入ったりして、何度も 400 になりました。

そこで、受け口の側で受け取れる形を広げました。

  • {"date": ..., "weight_body_mass": 65.0} のような平らな形を受け付ける(フォームだけで作れる)
  • "65.0 kg" のような単位付きの文字列でも、先頭の数字を読む
  • 日付は +0900・GMT+9・タイムゾーンなし(日本時間とみなす)・スラッシュ区切りのどれでも読む

iPhone の設定は一度作ったら触りたくないので、こちらのほうが楽でした。今のショートカットは「日付をフォーマット」も使わず、開始日をそのまま渡しています。

ただし、この受け取り方には前提があります。

  • 単位の文字は見ていません。 65.0 lb が来ても kg として入ります。ショートカット側で単位を kg・% に固定しているのはそのためです
  • タイムゾーンのない日付は日本時間とみなします。 日本で使う前提で、海外にいる日は時刻がずれます

/healthz はアプリに届かない

run.app のアドレスで GET /healthz を叩くと 404 になり、/status に変えると返ってくるようになりました。Cloud Run のドキュメントの既知の問題には、「末尾が z のパスには予約されているものがあるので、末尾が z のパスは避けることをすすめる」という注意書きがあります。原因は確かめきれていませんが、これに関係していると思います。

Secret の中身より先に Service を作ると失敗する

Cloud Run には、トークンを Secret Manager の「最新の版」として渡しています。Terraform で Secret と Service を一度に作ると、Secret に版がまだないので Service の作成が失敗しました。Cloud Run は、デプロイのときに Secret の版を確かめにいくためです。

先に Secret の箱だけを作り、トークンを入れてから Service を作っています。

terraform apply -target=google_secret_manager_secret.ingest_token
openssl rand -hex 32 | tr -d '\n' | gcloud secrets versions add ingest-token --data-file=-
terraform apply

別のやり方

ほかのアプリを経由しない:以前は、体重計 → ヘルスケア → MyFitnessPal → Garmin の経路で体重を送っていました。体脂肪率は入らず、MyFitnessPal を消した5月で止まりました。経由するアプリが多いほど止まりやすいので、ヘルスケアから直接送る形にしました。

指標を増やすなら Health Auto Export:ショートカットは、指標を1つ足すたびにアクションが増え、送れるのも「最新の1件」だけです。歩数や HRV のように1日に何件もある指標なら、Health Auto Export のほうが向いていると思います。受け口はこのアプリの JSON の形も受け取れるので、乗り換えてもサーバー側は変えなくて済みます。

使っている体重計

Eufy の体重計です。ヘルスケアに書き込める体重計なら、メーカーを問わず同じショートカットで送れます。

参考

あわせて読みたい

iPhone のヘルスケアをショートカットで Cloud Run に送る:400 が続いたので、受け口の側で受け取れる形を広げた

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

エンジニア。Claude Code / Codex / MCP / BigQuery / 自動化 / ガジェット / 3Dプリンター / スマートホーム。楽して生きたい。ゲーム / イヤホン / キーボード / プログラミング / AI

目次