背景・やりたいこと
- 小学生の娘の連絡帳はGoogleカレンダーに記載されており毎日Chromebookからそれを確認している
- 親もそれを確認しているが、見落とす事があり忘れ物が増えている
- 翌日分を前日に定期的に出力させたい
達成する為には
- 閲覧権限のみのGoogleカレンダーをPythonで取得する
- サーマルプリンタに値を渡す
- crontabなどで曜日指定で定期的に出力させる
試してみた
- Google Cloud でプロジェクト作成
- 「Google Calendar API」を有効化
デスクトップアプリ用の OAuth クライアントID を作成し、
credentials.jsonを作業ディレクトリに保存tokenを取得+カレンダーIDを見つける
import datetime
import os
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# 読み取り専用スコープ
SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"]
def get_credentials():
"""
Google Calendar API の認証情報を取得
- 初回はブラウザでOAuth認証
- 2回目以降は token.json から再利用
"""
creds = None
token_path = "token.json"
if os.path.exists(token_path):
creds = Credentials.from_authorized_user_file(token_path, SCOPES)
# 認証情報が無い or 無効ならログイン
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
"credentials.json", SCOPES
)
creds = flow.run_local_server(port=0)
# 次回以降のために保存
with open(token_path, "w") as token:
token.write(creds.to_json())
return creds
def list_calendars(service):
"""
自分が閲覧可能なカレンダー一覧を表示
共有カレンダーもここに出てくるので、summary と id をメモしておく
"""
calendar_list = service.calendarList().list().execute()
print("=== カレンダー一覧 ===")
for cal in calendar_list.get("items", []):
print(f"summary: {cal.get('summary')}, id: {cal.get('id')}")
print("======================")
def get_events(service, calendar_id, max_results=10):
"""
指定カレンダーの今以降のイベントを取得して表示
"""
try:
now = datetime.datetime.now(tz=datetime.timezone.utc).isoformat()
events_result = (
service.events()
.list(
calendarId=calendar_id,
timeMin=now,
maxResults=max_results,
singleEvents=True,
orderBy="startTime",
)
.execute()
)
events = events_result.get("items", [])
if not events:
print("イベントが見つかりませんでした。")
return
print(f"=== カレンダーID: {calendar_id} のイベント ===")
for event in events:
summary = event.get("summary", "(タイトル未設定)")
description = event.get("description", "(説明未設定)")
start = event["start"].get("dateTime", event["start"].get("date"))
end = event["end"].get("dateTime", event["end"].get("date"))
location = event.get("location", "(場所未設定)")
print(f"タイトル : {summary}")
print(f"説明 : {description}")
print(f"開始 : {start}")
print(f"終了 : {end}")
print(f"場所 : {location}")
print("-----")
except HttpError as error:
print(f"API エラー: {error}")
def main():
# 認証
creds = get_credentials()
service = build("calendar", "v3", credentials=creds)
# まずは自分が見えるカレンダー一覧を出す(共有カレンダー含む)
list_calendars(service)
# ここに、取得したい共有カレンダーIDを貼る
# 例: "xxxxxxx@group.calendar.google.com"
calendar_id = input("取得したいカレンダーIDを入力してください: ").strip()
# 予定取得
get_events(service, calendar_id, max_results=10)
if __name__ == "__main__":
main()
- 取得したtokenとカレンダーIDを使って下記で翌日分を取得します
import datetime
from zoneinfo import ZoneInfo # Python 3.9+
import json
import requests
from google.oauth2.credentials import Credentials
TOKEN_PATH = "token.json"
SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"]
CALENDAR_ID = "XXXXXXXXXXXXXXX@group.calendar.google.com"
TZ = "Asia/Tokyo"
def load_token_credentials():
"""
token.json から Credentials を復元
"""
creds = Credentials.from_authorized_user_file(TOKEN_PATH, SCOPES)
return creds
def refresh_if_needed(creds: Credentials):
"""
アクセストークンが切れていたら更新して token.json に保存
"""
if not creds.valid and creds.refresh_token:
from google.auth.transport.requests import Request as GoogleRequest
creds.refresh(GoogleRequest())
with open(TOKEN_PATH, "w") as f:
f.write(creds.to_json())
return creds
def get_day_range(target_date: datetime.date | None = None):
"""
target_date 当日の 00:00~翌日 00:00(Asia/Tokyo)の
timeMin, timeMax を RFC3339 文字列で返す
デフォルトは「明日」
"""
today = datetime.datetime.now(ZoneInfo(TZ)).date()
if target_date is None:
target_date = today + datetime.timedelta(days=1)
start = datetime.datetime.combine(
target_date,
datetime.time(0, 0, 0),
tzinfo=ZoneInfo(TZ),
)
# timeMax は「開始時刻の上限(排他的)」なので、
# 対象日の翌日 0:00 を指定する
end = start + datetime.timedelta(days=1)
return start.isoformat(), end.isoformat()
def get_tomorrow_events_raw(access_token: str):
"""
requests を使って Google Calendar API を直接叩く
"""
time_min, time_max = get_day_range() # デフォルトで「明日」
# print(f"timeMin: {time_min}")
# print(f"timeMax: {time_max}")
url = f"https://www.googleapis.com/calendar/v3/calendars/{CALENDAR_ID}/events"
params = {
"timeMin": time_min,
"timeMax": time_max,
"singleEvents": "true",
"orderBy": "startTime",
"maxResults": 50,
# 念のためレスポンスのタイムゾーンも固定したい場合
"timeZone": TZ,
}
headers = {
"Authorization": f"Bearer {access_token}",
}
resp = requests.get(url, headers=headers, params=params, timeout=5)
# print(f"HTTP {resp.status_code}")
if not resp.ok:
print(resp.text)
return None
return resp.json()
def show_events(events_json: dict):
items = events_json.get("items", [])
if not items:
print("指定日のイベントはありません。")
return
for event in items:
summary = event.get("summary", "(タイトル未設定)")
description = event.get("description", "(説明未設定)")
start = event["start"].get("dateTime", event["start"].get("date"))
end = event["end"].get("dateTime", event["end"].get("date"))
location = event.get("location", "(場所未設定)")
print(f"タイトル : {summary}")
print(f"説明 : {description}")
print(f"開始 : {start}")
print(f"終了 : {end}")
print(f"場所 : {location}")
print("-----")
def main():
creds = load_token_credentials()
creds = refresh_if_needed(creds)
events_json = get_tomorrow_events_raw(creds.token)
if events_json:
show_events(events_json)
if __name__ == "__main__":
main()
- 上記を前々回作成したfree.py(引数に出力したいテキストを受け取ってサーマル側で印刷)に渡してあげる
- 下記を参照
python3 gcal.py | python3 free.pyで問題無くサーマルプリンタから印刷が出来た!!- あとはiPhoneのショートカットで設定。これもssh+コマンドの組み合わせだけにした

- crontabの設定
0 19 * * 0-4 /home/pi/dev/thermal/venv/bin/python /home/pi/dev/thermal/gcal.py | /home/pi/dev/thermal/venv/bin/python /home/pi/dev/thermal/free.py
まとめ
- サーマルプリンタでやれる事が増えて活用度が上がった
- Google Calendarに情報を集約していけば更に活用が進みそう