試してみたブログ

AI関連・iPhone/Pixelなどのガジェット・音声入力・サーマルプリンタなど興味をある事をどんどん試してみた際の記録

さくらのメールボックスにcsvから自動で登録してくれるRPA的な拡張機能を作成した&学び

背景

  • さくらメールボックスにはなぜかcsvを使った一括登録が無く、手動で登録しないといけない。
  • 100件近く登録しないといけないケースが発生した
  • vive cordingで拡張機能をつかって解決出来そうだったのでやってみた

試してみた

  • Codex Cloudを使って作成
  • まずは導線の確認 「新規追加」、ユーザー名、パスワード、メールの受信、迷惑メールフィルターが必須項目であとは「作成する」

  • developer toolでそれぞれのDOMを調べてプロンプトへ渡す

  • csvにemail,password、メール受信についてのデータを作成
  • サクッと作れたが、、、数件はうまくいった物の、次の画面のレンダリング前にデータを記入しようとするのか、サーバー側からのレスポンスが遅いと5〜6件で止まってしまう
  • デバッグとの闘い。。。
  • 試行錯誤しまくり、機能も追加した上でなんとか出来た

github.com

気付き・学び

  • Chrome拡張を削除・登録・メニューバーを追加でやっていくのはしんどい
    • ショートカットキーを付けてメニューバーの追加作業を無くした方が楽
  • 初回の開発(精度8〜9割)に関してはCodex Cloudを使ってやるのが良いが、微調整や機能追加はCodex Cloudでやるよりも手元の開発環境内でやる方が早い
  • ログは大事なのでできる限り最初から出力するようにする
  • 一括処理は途中で止まる事も考慮して付け合わせ出来る様にする(リスト機能を開発)
  • 重複登録スキップ、削除機能も考えておく必要がある
  • 画面遷移するとログが消えたり、その後の実行が無くなったりするのでそもそもの挙動をちゃんと把握しておく必要がある
  • アラートなど一部制御出来ない部分がある(やり方次第?)
  • DOMを渡すのにもっと良い方法があるので調べる
  • 構造が変わればすぐに使え無くなる物なので、サクッと出来なかったら早めに諦める(手動でやった方が早い)

「AI時代に取り残されないために、毎日30分新しいAIツールを試すことを30日間続けてみた」の記事を読んで

背景

  • 下記の「AI時代に取り残されないために、毎日30分新しいAIツールを試すことを30日間続けてみた」を読んだ

dev.classmethod.jp

感想

  • 自分も全く同じ悩みからの試してみたブログを作成したので、取り残されない様という気持ちに共感した
  • 気がつけばいつも同じツールばかり使っている ここにも強い共感
  • 事前準備、実践検証、振り返りと3つのフェーズに別けているのがよい
  • ちゃんと振り返りが出来ている
  • 是非この次のフェーズである、"実際に取り入れてみた"部分を大事にしていきたい

Google Mapで任意の幅・高さの物で埋め込みコードを取得出来る拡張機能を作成した

背景

  • 以前、下記でWordpress用のプラグインとしてGoogle Mapの埋め込みコードを取得する物を作成した
  • WPの管理画面からMap取得する導線分が余分で、スマホで使うぐらいでMacで作業する際にはほぼ使わなかった
  • いっその事、拡張機能でサクッと取得出来る様にした

tameshitemita.hatenablog.jp

試してみた

  • いつものCodex Cloudで作る
  • 別のブログで使っている幅・高さが820x615の為、固定する
  • プロンプト
拡張機能で住所や建物名などを入れて該当する物の埋め込みコードをクリップボードにコピーする物を作成したい。
複数ある場合は住所情報を出して、google mapへのリンクを出して、確認出来る様にして。また確認後、どちらでも埋め込みコードが取得ができるように。
またソースをクリックしたら、クリップボードに入る様にする。
820x615の埋め込みサイズにして

また使い勝手を良くするための2点修正をお願いします
1.拡張機能を開いたら検索テキストエリアにフォーカスが当たった状態にしてください
2.command+Shift+eで拡張機能が開ける様にショートカット設定してください
  • 下記が出来た!

  • UIもいい感じです!

振り返り

  • WPで作るからプラグインが良いと思っていたが、実際の作業ではほぼMacなので普段使う方に寄せる方が良かった事に気がついた
  • 拡張機能のショートカットはいろいろ被っているので、どれにするか今後検討が必要
  • Codex Cloudはサクッとつくれるので良い

Googleカレンダーの連絡帳を取得して定期的にサーマルプリンタから印刷する

背景・やりたいこと

  • 小学生の娘の連絡帳は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(引数に出力したいテキストを受け取ってサーマル側で印刷)に渡してあげる
  • 下記を参照

tameshitemita.hatenablog.jp

  • 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に情報を集約していけば更に活用が進みそう

買い物リストGoogle Keepの「My shopping list」をiPhoneからワンタップでサーマルプリンタから印刷する

背景

tameshitemita.hatenablog.jp

  • 前回、前準備として買い物リストをGoogle KeepのMy shopping listに集約をした
  • またブラックフライデーでサーマルプリンタを購入し、現在サーマルプリンタの活用を実施している

tameshitemita.hatenablog.jp

やりたいこと

  • Google KeepのMy shopping listをiPhoneからワンタップでサーマルプリンタから印刷したい

そのためには

  • raspberrypi上のpythonからGoogle Keepからデータを取ってくる
  • 取ってきたデータをサーマルプリンタで印刷する
  • iPhoneからショートカットを使ってraspberrypiに接続してpythonを実行する

試してみた

  • Pythonから直接扱うなら非公式のライブラリのgkeepapiを使うのが良さそう
  • 認証情報周りが思ったよりも複雑
  • master tokenを取得する必要がある

qiita.com

  • 上記を参考にmaster tokenを取得
  • 詰まったポイントとしては、下記の3点

    • OAuth Tokenの取得方法の際に画面が遷移しないが、そのままdeveloper toolでOAuthを取得する
    • Android IDはIMEIの番号を使えば良い(Pixelの設定画面から取得が出来た)
    • 実行して取得出来たtokenがめちゃ長いが、よくよくparseするとtokenは200文字程度なので注意深く見ること。他にもいろいろと情報があるのでそこは捨てる
  • 作成したコードは下記。指定したIDのノートを取得して出力する

import gkeepapi

EMAIL = "XXXXXXX@gmail.com"
MASTER_TOKEN = "aas_et/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
NOTE_ID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

keep = gkeepapi.Keep()

# 認証
keep.authenticate(EMAIL, MASTER_TOKEN)
keep.sync()  # ノート同期[web:5]

# IDからノート取得
note = keep.get(NOTE_ID)
if note is None:
    print("指定したIDのメモが見つかりませんでした。")
    exit(1)

# 未チェックの項目だけを抽出
unchecked_items = []
for item in note.items:  # チェックリストの各行にアクセス[web:32][web:96]
    if not item.checked:
        unchecked_items.append(item.text)

print(note.title)
for t in unchecked_items:
    print("[ ]", t)
  • 上記を前回作成したfree.py(引数に出力したいテキストを受け取ってサーマル側で印刷)に渡してあげる
  • 下記を参照

tameshitemita.hatenablog.jp

  • python3 gkeep.py | python3 free.py で問題無くサーマルプリンタから印刷が出来た!!
  • あとはiPhoneのショートカットで設定。これもssh+コマンドの組み合わせだけにした

今後の課題

  • 買い物リストにちゃんと集約を徹底する(メモやtodoistなどにメモしない)
  • 印刷した物をスマホの裏に貼り付けるなど、買い物行く前に印刷して持っていく様な導線を作る
  • 非公式のライブラリなので、不意に動かなくなるリスクがあるので、そこで運用を辞めてしまわない様にする

買い物リストをGoogle Keepの「My shopping list」に集約する

昨日のエントリーで買い物リストをサーマルプリンタから出力をしたい熱が出て来たのでまぜは前段の準備から行う

tameshitemita.hatenablog.jp

事前準備でやること

  • 買い物リストを1つにまとめる

    • 現在は、Todoistを使ったりiPhoneのメモに記載したりGoogle Docsに記載したりとかなりばらついている
    • せっかくサーマルプリンタに出力する際に情報が歯抜けになっていると微妙なので1つに集約していく
    • 今回はキッチンにあるGoogle Homeを通じて買い物リストを追加する
  • iPhoneでの記載もGoogle Keepの買い物リストに置き換える

準備

  • Google Homeの買い物リストの追加先を調べる
    • 前はGoogle Taskだった記憶があるが
    • 現在はGoogle Keepの「My shopping list」に移行されたみたい

iPhone側でアイコン追加

  • iPhoneからワンタップでこのMy shopping listが開く様にショートカットを設定する

  • 「ホーム画面」に追加でワンタップで買い物リストをGoogle Keepで表示できる様にした

Pixel側でウィジェット

  • アイコンタップでMy shopping listに飛ぶ良い方法が無いのでいったん断念

まとめ

iPhone/Pixle側からの入力とGoogle Homeからの入力を統一する事で、買い物リストを1つにまとめた。次回はサーマルプリンタへの出力!

サーマルプリンタを個人で使った事例や今後やりたいことをまとめてみる

今後やりたいこと

  • 上記にまとめてみた
  • まずは時間割、買い物リストあたりから進めて行く(次回以降)
  • せっかくのおもちゃなのでバリバリ使って行きたい
  • デジタルをアナログにして人間の物理的なリマインドが主になりそう

事例まとめ

perplexityで収集した情報をもとに、サーマルプリンタを個人で使ったおもしろい事例をレポートにまとめます。


サーマルプリンタの個人利用における興味深い活用事例 10選

サーマルプリンタはインクを必要としない熱転写式の印刷装置で、個人の趣味や日常生活において創意工夫されたさまざまな使い方が広がっています。以下、実際に報告されている10の興味深い活用例を紹介します。

1. 同人誌製作時の納品書・ありがとうシール作成

同人誌や手作り作品を通販で送付する際に、サーマルプリンタで納品書やお礼のシールを自作するという活用法があります。オリジナルの納品書をシール紙に印刷して、配送物に貼り付けることで、商品の中身を明確にするとともに、入れ忘れ防止をチェックボックスで実現しています。さらに、キャラクターイラストを使った「ありがとうございました」シールをカットして、ダイカットステッカーのような仕上がりを実現する活用例も報告されています。[1]

2. 手帳・手作りカード用のイラストカード作成

きれいな背景デザインのシールをメルカリなどで入手し、スキャンした画像やダウンロードした背景マステのシールを組み合わせて、コラージュ風のオリジナルカードを作成する事例があります。手書きでは難しい複雑なデザインをシール状に印刷して、手帳やノートに貼り付けることで、高品質な装飾が実現できます。[1]

3. やることリスト・買い物リストの個人カスタマイズ

無印良品や100均のTODOリストは紙質が良すぎたり、デザインが気に入らなかったりすることがあります。サーマルプリンタを使えば、レシート用紙と同じ質感のテンプレートに自分好みのチェックボックス付きリストを印刷できます。ショウもないリスト内容に対して、あえて気軽に処分できる紙質にすることで、気分なくリスト作成できる実用的な活用が実現できます。[1]

4. 冬期限定の配送用宛名ラベル作成

メルカリなどで郵送する際の宛名ラベルをサーマルプリンタで作成する実例があります。夏場は配送トラック内の温度上昇により感熱紙が黒ずむ可能性があるため、冬場限定での利用が推奨されています。通常のラベル印刷よりもコスト効率が良く、Aiビスペイントで正方形キャンバスに「郵便番号」「住所」「名前」を配置する工夫で、バランスの取れたラベルが実現できます。[1]

5. 読書記録用の書影(本の表紙画像)の印刷活用

読書メモやマインドマップを作成する際に、書店から書影画像をスキャンして、90度回転させ「ちょうどいいサイズ感」で印刷し、手帳の読書リストページに貼り付ける使用例があります。セントラルイメージとして機能させることで、視覚的に読書記録の質が向上します。手書きだけでは実現できない情報量と精密さが、細かい文字で手帳ページを完成させるのに役立ちます。[2]

6. 園・学校からのお便り情報の一元化

子どもの学校や保育園から配布されるプリント類をスマートフォンで写真撮影し、サーマルプリンタで印刷して手帳やカレンダーに貼り付ける活用法があります。転記の手間がなく、情報を一元管理でき、重要な行事予定を視覚的に把握しやすくなります。[2]

7. SNS・メモアプリの内容をスクラップ

X(旧Twitter)やメモアプリに記録した日々の成果や覚書を画像化して印刷し、手帳やノートに直接貼り付ける実例があります。デジタル記録を物理的な手帳に統合することで、思考整理が促進され、後で見返すときに手帳ベースで完結します。[2]

8. 家具・家電などの購入検討時の比較資料作成

家具や電化製品の購入を検討する際に、Webサイトのスクリーンショットを印刷して、ノートに並べて貼り付ける活用があります。スペック、価格、機能を一覧性よく比較でき、デジタル上のブックマークやスクショ管理よりも検討しやすいという報告があります。[2]

9. 子ども向けキャラクターステッカーの自作

お子さんが好きなキャラクターをスキャンして印刷し、シール状にして自作ステッカーにする例が挙げられます。子ども自身の文房具に貼ったり、友人への手紙に貼ったりする活用が報告されており、家庭内での手軽なDIYプロジェクトになっています。[2]

10. 占い・人生設計用の星占い情報の可視化

占い師・石井ゆかりさんの著作「3年の星占い」など、テキストベースの情報を「マイクロ文字印刷」機能で小さな文字で印刷し、手書きでは実現困難な情報量を手帳1ページに集約する活用例があります。複数年の人生計画を俯瞰的に把握できるようになり、手帳の質的価値が向上します。[2]


共通する活用のポイント

これらの事例から見える共通点は、サーマルプリンタが「手軽で低コスト」「カスタマイズ可能」「物理的記録」の3点で優れていることです。特に、スマートフォンと連携するアプリを通じて、デジタル情報を瞬時にアナログ記録に変換できることが、個人の創意工夫を促進しています。季節や用途に応じた柔軟な使い分けと、テンプレートの活用により、日々の生活の質を高める個人的な文房具利用が実現されています。[3]

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

上記の所感

  • スクラップボックス化は良いかもしれない。あえてアナログ化する事で印象づけるのは手
  • マスキングテープで何かに貼るというのを取り入れる