GitHub Actions + Google Maps Route API + iPhoneショートカットで朝の渋滞を検知する
概要
毎朝の通勤で「どのルートが今日は空いてるか」を確認するのが面倒だったので、
ボタン一発でSlackに結果を通知するシステムを作った。
構成:
- Google Maps Routes API → 複数ルートの渋滞考慮済み所要時間を取得
- Python → ルート比較スクリプト
- GitHub Actions → 実行エンジン
- Slack Incoming Webhook → 結果通知
- iOSショートカット → ワンタップで起動
必要なもの
- Google Cloud アカウント(Routes API有効化 + APIキー)
- GitHub アカウント(リポジトリ + GitHub Mobile アプリ)
- Slack ワークスペース(個人用でOK)
- iPhone(ショートカットアプリ)
Step 1: Google Maps Routes API のセットアップ
- Google Cloud Console でプロジェクト作成
- 「Routes API」を有効化
- 「認証情報」→ APIキーを発行
Step 2: Pythonスクリプトの作成
ディレクトリ構成
.
├── check_route.py
├── requirements.txt
├── .env
├── .env.example
├── .gitignore
└── .github/
└── workflows/
└── check_route.yml
requirements.txt
requests
python-dotenv
check_route.py
import json
import os
import sys
import requests
from datetime import datetime, timezone, timedelta
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("GOOGLE_MAPS_API_KEY")
ROUTES_API_URL = "https://routes.googleapis.com/directions/v2:computeRoutes"
HOME = os.getenv("HOME_ADDRESS")
WORK = os.getenv("WORK_ADDRESS")
def load_routes():
routes = []
i = 1
while True:
raw = os.getenv(f"ROUTE{i}")
if not raw:
break
data = json.loads(raw)
routes.append({
"name": data["name"],
"waypoints": [HOME] + data["waypoints"] + [WORK],
})
i += 1
return routes
def fetch_route(name, waypoints):
body = {
"origin": {"address": waypoints[0]},
"destination": {"address": waypoints[-1]},
"travelMode": "DRIVE",
"routingPreference": "TRAFFIC_AWARE",
}
intermediates = waypoints[1:-1]
if intermediates:
body["intermediates"] = [{"address": wp} for wp in intermediates]
headers = {
"Content-Type": "application/json",
"X-Goog-Api-Key": API_KEY,
"X-Goog-FieldMask": "routes.duration,routes.distanceMeters",
}
res = requests.post(ROUTES_API_URL, json=body, headers=headers, timeout=10)
if not res.ok:
return {"name": name, "error": f"{res.status_code}: {res.text}"}
route = res.json()["routes"][0]
duration_sec = int(route["duration"].rstrip("s"))
return {
"name": name,
"duration_min": duration_sec // 60,
"duration_sec": duration_sec % 60,
"distance_km": route["distanceMeters"] / 1000,
}
def notify_slack(message):
webhook_url = os.getenv("SLACK_WEBHOOK_URL")
if not webhook_url:
return
requests.post(webhook_url, json={"text": message}, timeout=10)
def main():
JST = timezone(timedelta(hours=9))
now = datetime.now(JST).strftime("%Y-%m-%d %H:%M")
results = [fetch_route(r["name"], r["waypoints"]) for r in load_routes()]
valid = [r for r in results if "error" not in r]
fastest = min(valid, key=lambda r: r["duration_min"] * 60 + r["duration_sec"])
lines = [f"*朝の通勤ルートチェック ({now})*\n"]
for r in valid:
marker = " :star: 最速!" if r["name"] == fastest["name"] else ""
lines.append(f"{r['name']}{marker}")
lines.append(f" {r['duration_min']}分{r['duration_sec']:02d}秒 {r['distance_km']:.1f} km")
notify_slack("\n".join(lines))
if __name__ == "__main__":
main()
経由地の住所をGoogleマップで調べる
ルートの経由地に指定する住所は、Googleマップで以下の手順で確認できる。
- Googleマップで、自宅 → 経由地1 → 経由地2 → 職場 のルートを検索(ルート検索で経由地を追加)
- 表示されたフォームの住所をコピー
-
.envのROUTE{n}のwaypointsに貼り付ける - GithubのSecretsにも同様に登録する
ランドマーク(駅・公共施設など)は施設名でも認識されることが多いが、住所の方が確実。

.env.example
GOOGLE_MAPS_API_KEY=your_api_key_here
HOME_ADDRESS=your_home_address_here
WORK_ADDRESS=your_work_address_here
ROUTE1={"name": "ルートA", "waypoints": ["経由地1", "経由地2"]}
ROUTE2={"name": "ルートB", "waypoints": ["経由地1"]}
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX/YYY/ZZZ
.gitignore
.env
__pycache__/
*.pyc
.venv/
Step 3: Slack Incoming Webhook の設定
- api.slack.com/apps → 「Create New App」→「From scratch」
- 左メニュー「Incoming Webhooks」→ トグルを ON
- 「Add New Webhook to Workspace」→ チャンネルを選択
- 表示されたWebhook URLをコピー
動作確認:
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"テスト通知"}' \
https://hooks.slack.com/services/XXX/YYY/ZZZ
Step 4: GitHub Actions の設定
Secrets の登録
リポジトリの Settings → Secrets and variables → Actions → New repository secret で追加:
| Name | Value |
|---|---|
GOOGLE_MAPS_API_KEY |
Google CloudのAPIキー |
SLACK_WEBHOOK_URL |
SlackのWebhook URL |
HOME_ADDRESS |
自宅の住所 |
WORK_ADDRESS |
職場の住所 |
ROUTE1 |
ルート1の名前と経由地(JSONオブジェクト) |
ROUTE2 |
ルート2の名前と経由地(JSONオブジェクト) |
ROUTE3 |
ルート3の名前と経由地(JSONオブジェクト) |
ルート名と経由地を1つのJSONオブジェクトにまとめて登録する:
{"name": "ルートA", "waypoints": ["経由地の住所1", "経由地の住所2"]}
ROUTE1 から連番で定義し、存在しない番号になった時点でループを終了する仕組みのため、
ルートを追加したい場合は ROUTE4、ROUTE5... とSecretsを増やすだけでよい。
.github/workflows/check_route.yml
name: 朝の通勤ルートチェック
on:
workflow_dispatch:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements.txt
- run: python check_route.py
env:
GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
HOME_ADDRESS: ${{ secrets.HOME_ADDRESS }}
WORK_ADDRESS: ${{ secrets.WORK_ADDRESS }}
ROUTE1: ${{ secrets.ROUTE1 }}
ROUTE2: ${{ secrets.ROUTE2 }}
ROUTE3: ${{ secrets.ROUTE3 }}
Actionsタブの「Run workflow」から手動実行して動作確認する。
Step 5: iOSショートカットの設定
GitHub Mobile アプリを使うのが最も簡単。
- App StoreでGitHub Mobileをインストール
- ショートカットアプリ → 「+」→「アクションを追加」
- 「ワークフローをディスパッチ」を検索して追加
- 各フィールドを入力:
| フィールド | 値 |
|---|---|
| Owner | GitHubユーザー名 |
| Workflow ID | check_route.yml |
| Repository | リポジトリ名 |
| Branch / ref | main |
- ショートカットをホーム画面に追加
ハマりどころ
1. departureTime に現在時刻を渡すと400エラー
Routes APIは departureTime に過去・現在時刻を渡すと弾かれる。
"Timestamp must be set to a future time."
routingPreference: TRAFFIC_AWARE を指定するだけで現在の渋滞を考慮してくれるので、
departureTime は省略でOK。
2. iOSショートカットの「URLの内容を取得」で204エラー
GitHub workflow_dispatch APIは成功時に 204 No Content を返す。
iOSショートカットはレスポンスボディが空だとエラーと誤認することがある。
解決策1: GitHub Mobileの「ワークフローをディスパッチ」アクションを使う(推奨)
解決策2: リクエストボディに return_run_details: true を追加すると
200+ボディで返ってくるようになる(2026年2月〜対応)
{
"ref": "main",
"return_run_details": true
}
3. APIエラーの詳細が見えない
requests.raise_for_status() だとエラーメッセージが隠れる。
デバッグ中は res.text を出力するようにすると原因がわかりやすい。
if not res.ok:
return {"name": name, "error": f"{res.status_code}: {res.text}"}
まとめ
| 項目 | 採用技術 |
|---|---|
| ルート取得 | Google Maps Routes API(TRAFFIC_AWARE) |
| スクリプト | Python + requests |
| 実行基盤 | GitHub Actions(workflow_dispatch) |
| 通知 | Slack Incoming Webhook |
| スマホトリガー | iOSショートカット + GitHub Mobile |
朝の出発前にボタン一発で最速ルートがSlackに届くようになった。
GitHub Actionsは無料枠(月2000分)で十分運用できる。
ソースコード
Discussion
ショートカットのオートメーションで、ナビのBluetooth接続時をトリガーにすれば完全自動化もできそうですね