孤独クラフト / Solo Craft

引きこもり非エンジニアの製作日記

GASとESP32で外出先からPCを起動する【第3部】

はじめに

本シリーズでは,Google Apps Script (GAS) と ESP32 を使って,外出先から自宅のPCをリモートで起動する方法を紹介しています。あくまで個人的な実践経験を共有することが目的のため正確性に欠ける部分があるかもしれませんが,参考にしていただければ幸いです。

前回の振り返り

▼ 前回の投稿 yhotta240.hatenablog.com

前回は,Google Apps Script(以下GAS)を使ってプロジェクトを作成し,APIをデプロイするところまで進めました。今回は,Apps Script APIを外部から呼び出すために必要な情報と実際にAPIをどのように呼び出していくかを具体的に見ていきます。

クライアントアプリの作成

APIを呼び出す作業に入りたいところですが,まずはクライアントアプリを作成します。ここでいうクライアントアプリとは,アプリケーションやデバイスのことです。GASではHTMLを扱うことができ,PCを起動するスイッチなどを作ってもいいのですが,HTMLを書くのはちょっと面倒ですし,ファイルのアップロードができないため,ここではAppSheetを使うことにしました。AppSheetはノーコードで簡単にウェブアプリを作成できるツールです。

まず,Google Sheetsに戻り,拡張機能から「AppSheet」を選択して作成を始めます。

Google Sheet
AppSheet

すると,AppSheetの編集ページが表示されます。これで基本的には完成です。AppSheetはGoogle Sheetsをデータベースとして参照し,自動的にウェブアプリを作成してくれます。非常に簡単にアプリを作成できるので非常に便利です。

アプリができたらブラウザで開いてみましょう。このアプリは自分のアカウントでログインしないと見れないので,自分だけのアプリが完成したということです。また,スマホアプリを使ってログインすれば,スマホでも同じアプリを利用できます。もちろん,値を変更してSaveするとGoogle Sheetの内容も自動的に更新されます。

Desktop版

これでクライアントアプリの作成は完了です。

OAuth 2.0のフロー概要

Apps Script APIを呼び出す前にOAuth 2.0について確認しておきましょう。GoogleAPIでは認証と認可に OAuth 2.0 プロトコルを使用します。つまりAPIを呼び出すには,OAuth認証を用いて認可コードを取得し,その後アクセストークンを取得する必要があります。

まず,クライアントアプリケーションは認可サーバに対して認可コードの取得を要求します。認可サーバはユーザに対して「このクライアントアプリケーションに権限を与えてもよいですか?」と尋ねます。ユーザが「OK」を選択すると認可サーバは認可コードを発行し,クライアントアプリケーションに返します。その後,クライアントアプリケーションは取得した認可コードを用いてアクセストークンを取得します。この一連の流れがOAuth 2.0認証の基本的な流れです。

次にクライアントアプリケーションは,取得したアクセストークンを用いてリソースサーバのAPIにアクセスします。APIリクエストには,アクセストークンを含める必要があります。これにより,Apps Script APIへのアクセスが認証され,適切なデータを取得することができます。

全体の流れ

OAuth 2.0のフロー概要からAPIを利用するために必要なパラメータと,その全体的な流れを以下に整理します。必要なパラメータでは,それぞれどこで取得できるか()内に書いています。

必要なパラメータ

流れ

  1. 認可コードを取得

    • 必要パラメータ:クライアントID,スコープ,リダイレクトURI(承認済みURI
    • 認可サーバにリクエストを送り,ユーザーに承認を求めます。承認されると,認可コードが返されます。
  2. アクセストークンとリフレッシュトークンの取得

    • 必要パラメータ:クライアント ID,クライアントシークレット,リダイレクトURI,認可コード,スコープ(省略可)
    • 認可コードを使って,アクセストークンとリフレッシュトークンを取得します。これにより,APIリクエストを認証できるようになります。
  3. Apps Script API を呼び出す(GAS関数を実行)

    • 必要パラメータ:アクセストークン,実行可能API,実行する関数の指定(JSON形式)
    • 取得したアクセストークンを使用して,Apps Script APIにリクエストを送り,指定した関数を実行します。
  4. アクセストークンの更新

    • 必要パラメータ:クライアントID,クライアントシークレット,リダイレクトURI,リフレッシュトーク
    • アクセストークンの有効期限が切れたらリフレッシュトークンを使って新しいアクセストークンを取得します。
  5. 再度Apps Script API を呼び出す

    • 必要パラメータ:アクセストークン,実行可能API,実行する関数の指定(JSON形式)
    • 新しいアクセストークンを使って,再びApps Script APIにリクエストを送り,必要な関数を実行します。

developers.google.com

Apps Script APIの概要

Apps Script APIは,GASのプロジェクトや機能に対してプログラムから操作や情報の取得を行うための仕組みです。RESTful APIという設計方式を採用しており,HTTPリクエスト(GET、POSTなど)を送信することでGASの機能を呼び出したり,プロジェクトに関する情報を取得したりできます。

主なリソースとその役割

Apps Script APIは,複数のリソース(機能のグループ)で構成されています。

  • projects: スクリプトプロジェクトを操作します。
  • projects.deployments: プロジェクトを「デプロイ」(公開・配布)した際の状態を管理します。
  • projects.versions: プロジェクトの「バージョン」を管理します。
  • processes: スクリプトの実行プロセスを管理します。
  • scripts: これが最もよく使われる機能で、Apps Scriptの関数をリモートから実行するためのエンドポイントです。例えば,特定のGAS関数を外部から実行して,その結果を受け取ることができます。

今回GAS関数を外部から実行したいので scriptリソース を用います。

▼ 以下の記事でscriptsリソースについてまとめています。 zenn.dev

APIを叩いてみる

それでは実際にAPIを叩いてみましょう。ここで「APIを叩く」とは,APIを呼び出すことを意味します。いきなりESP32からAPIを呼び出すプログラムを書くのは難しいかもしれませんし,急にC++Pythonのコードが提示されても,その動作をイメージするのは難しいでしょう。

Apps Script APIはHTTP通信を使用するため,まずはインターネット環境を通じてAPIが正しく動作するかをテストする必要があります。これにより,実際のプログラムを書く前にAPIが正しく応答するか確認できるので,スムーズに開発を進めることができます。

OAuth 2.0 PlaygroundでAPIを叩く

HTTP通信のテストには,GUIグラフィカルユーザーインターフェース)やCUI(キャラクターユーザーインターフェース)の方法があります。ここでは,OAuth 2.0 Playgroundを活用することでGUI形式で簡単にAPIをテストすることができます。

OAuth 2.0 Playgroundは,GoogleのOAuth 2.0認証フローを使用してAPIを試すためのツールです。
以下のリンクからアクセスできます:

developers.google.com

このツールを使うことで,アクセストークンを取得し,実際にAPIリクエストを送信して応答を確認することができます。これにより,APIの動作確認やトラブルシューティングが容易になります。

まず,Google Cloud コンソールからクライアントIDとクライアントシークレットを取得します。これらは,認証情報を作成したときにメモをしていたと思います。もし忘れていた場合は,Google Cloud コンソールの「APIとサービス」> 「認証情報」のOAuth 2.0 クライアントIDを作成した場所にあります。

console.cloud.google.com

次に,OAuth 2.0 Playgroundの設定を開きます。「Use your own OAuth credentials」にチェックを入れると,クライアントIDとクライアントシークレットを入力する欄が表示されるので,ここにそれぞれペーストします。

OAuth 2.0 Playground

その後,「Step 1 Select & authorize APIs」のセクションで,APIのスコープを選択します。スコープとは、アプリケーションがアクセスするリソースや権限を定義するものです。「Apps Script API v1」を探し,タブを開くとスコープが表示されます。そこから「https://www.googleapis.com/auth/spreadsheets」を選択し,「Authorize APIs」ボタンをクリックします。

ここで認証画面が表示され,アカウントの認証を行うはずですが,エラーが発生することがあります。

これはOAuth 2.0 Playgroundがクライアントとして機能しているため,認可サーバにそのクライアントを正しく登録していないことが原因です。

認証情報を作成する

この問題を解決するには,Google Cloud Consoleの「認証情報」セクションで「承認済みのリダイレクト URI」を設定する必要があります。

承認済みのリダイレクト URI

ここに「https://developers.google.com/oauthplayground」をペーストしてください。URLの最後に「/」が含まれてしまうと認証が失敗するので,その点にも注意が必要です。ユーザーが認証されると指定したリダイレクトURIにリダイレクトされ,そのページに遷移することになります。

認可コードを取得

OAuth 2.0 Playgroundに戻り,再びスコープを選択して認証を進めます。

HTTP/1.1 302 Found
すると,Request / Responseのところに「HTTP/1.1 302 Found」というメッセージとLocationが表示されます。そして,その下にレスポンスメッセージとして「GET」が表示されたらOKです。レスポンスには認可コードが含まれているので,これを次のステップで使用します。

アクセストークンの取得

次に,「Step 2 Exchange authorization code for tokens」のセクションに進みます。ここでは,先ほど取得した認可コードを使ってアクセストークンに交換します。「Exchange authorization code for tokens」ボタンをクリックすると,アクセストークンとリフレッシュトークンが生成されます。

アクセストークンとリフレッシュトーク

  • リフレッシュトークン(Refresh token):アクセストークンの有効期限が切れた後,新しいアクセストークンを取得するために使用されるトークンです。
  • アクセストークン(Access token):Google APIにアクセスするためのトークンで,有効期限が約1時間(3600秒)です。

リフレッシュトークンは,アクセストークンが無効になった後でも再度新しいアクセストークンを取得するために必要です。これにより,ユーザは再認証せずにAPIを継続して利用できます。

Apps Script API の呼び出し

最後に「Step 3 Configure request to API」では,実際にAPIへHTTPリクエストを送信します。

Apps Script APIのscriptsリソース ここでは,外部からGAS関数を実行したいので,Apps Script APIのscriptsリソースを使用します。リクエストは以下のようになっています。
HTTP リクエストの形式:

  • HTTP メソッド: POST
  • リクエスURI: https://script.googleapis.com/v1/scripts/{scriptId}:run

リクエストボディの構成
リクエストボディには次のようなJSON形式でデータを記述します:

{
  "function": string,
  "parameters": [
    value
  ],
  "sessionState": string,
  "devMode": boolean
}

HTTP リクエス
まず,HTTP Methodを「POST」に設定します。そして,Request URIには「実行可能API」のURLを入力します。次に,「Enter request body」には,実行するGAS関数の内容をJSON形式で記述します。

たとえば,getCellValue関数を実行したい場合は以下のように記述します:

{'function': 'getCellValue'}

setCellValue関数を実行したい場合は,以下のように記述します:

{
  "function": "setCellValue",
  "parameters": ["TRUE"],
}

ここでは,パラメータとしてTRUEFALSEAPIで送信することで,GASが指定した関数を実行してGoogle Sheetsの値を更新してくれます。これにより,APIを呼び出してGoogle Sheetsのデータを操作することが可能になります。

最後に「Send request」ボタンをクリックします。レスポンスに「HTTP/1.1 200 OK」と表示されれば,リクエストは正常に送信されて処理が成功したことを示します。

APIへのリクエスト/レスポンス

そして,レスポンス内容には以下のようなJSONが表示されるはずです:

{
  "done": true, 
  "response": {
    "@type": "type.googleapis.com/google.apps.script.v1.ExecutionResponse", 
    "result": false
  }
}

この "result" の部分には,GASで定義した関数の return 値が返ってきます。たとえば,getCellValue 関数を実行した場合,Google Sheetsの特定セルの値がここに表示されるでしょう。

これで,OAuth 2.0 Playgroundを使ってAPIをHTTPで叩くテストが無事に完了しました。



CUIAPIを叩く

CUIAPIを叩く方法もあるので紹介しておきます。主にBashコマンドプロンプトでリクエストを送信する際に使用します。PowerShellも使えますが,書き方が少し複雑なのでここでは省略します。

Bashコマンドプロンプトでは,cURL(Client URL)というコマンドラインツールを使ってHTTPリクエストを送信します。cURLは非常に強力で,さまざまなプロトコルをサポートしているため,APIテストやスクリプトの一部としてよく利用されます。

新たな認証情報を作成する

まず,認可コードの取得ですが,これはcURLでは送信できないため,任意のブラウザでURLにアクセスしてリダイレクトされたページのURLから認可コードを取得します。

ただ,OAuth 2.0を使用するためにはクライアントを設定する必要があります。クライアントを設定する際に「承認済みのリダイレクト URI」を指定する必要があります。これは,ユーザーがGoogleの認証ページでログインを完了した後にリダイレクトされる先のURLです。このリダイレクトURIは事前にクライアント設定で登録しておく必要があります。

例えば,OAuth 2.0 Playgroundを使う場合は,リダイレクトURIhttps://developers.google.com/oauthplaygroundを設定しましたが,ローカル開発環境でテストを行う場合はhttp://localhost:3000のように設定することができます。

今回の場合,クライアントはAppSheetとして既に作成してあるので,そのURLをリダイレクトURIとして使います。まず,ブラウザで開いているAppSheetのURLをコピーしてください。
例えば,以下のようなURLになっていると思います: https://www.appsheet.com/start/***?platform=desktop#vss=*****-**-**-**

ちょっと長いURLですが,リダイレクトURIに使う部分は ?platform=desktop の直前までの https://www.appsheet.com/start/*** の部分だけです。?platform=desktop#vss=*****-**-**-** の部分はプラットフォーム情報なので,ここでは不要です。

そして先述の通り,Google Cloud Consoleの「認証情報」セクションで「承認済みのリダイレクト URI」を設定する必要があります。前回,認証クライアントを作成しましたが,また新しい認証クライアントIDを作成します。

新たな認証クライアント

適当に名前を決めて,承認済みのリダイレクト URIhttps://www.appsheet.com/start/*** をペーストして追加してください。

承認済みのリダイレクト URI

新たなクライアントIDとクライアントシークレットも忘れずにメモしておきましょう。

認証情報

認可コードを取得

設定が完了したら,そのリダイレクトURIを使って認可コードを取得します。

URLは次のようになります(わかりやすいように改行しています):

https://accounts.google.com/o/oauth2/v2/auth?
  scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fspreadsheets&
  access_type=offline&
  include_granted_scopes=true&
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=YOUR_REDIRECT_URI&
  prompt=consent
  • YOUR_CLIENT_ID クライアンID
  • YOUR_REDIRECT_URI リダイレクトURI(AppSheet)
    を変更してください。

このURLにアクセスすると,Googleアカウントの認証ページが表示され,承認するとAppSheetのページにリダイレクトされるはずです。 AppSheetのURLを見てみるとこのようになっています。

https://www.appsheet.com/start/***?code=***&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fspreadsheets&platform=desktop#***-24-09-10

AppSheetのアプリのURLにcode=***scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fspreadsheetsが追加されていることが分かります。 このURLの中で,code=*** という部分が認可コードになります。この部分をメモしておいてください。

ちなみに認可コードを取得する際のURLでは,access_type=offlineprompt=consentを含めることで,リフレッシュトークンも同時に取得できます。access_type=offlineはリフレッシュトークンを取得するための設定であり, prompt=consentはユーザーに再度同意を求めるための設定です。公式ドキュメントでは,access_type=offlineを指定することでリフレッシュトークンを取得できるとされていますが,実際にはユーザーに再度同意を求める必要があるため,少しややこしく感じるかもしれません。prompt=consentを併せて指定することで,ユーザーに明示的に再認証を促し,確実にリフレッシュトークンを取得することができます。

アクセストークンの取得

次に,cURLを使ってアクセストークンとリフレッシュトークンを取得します。cURLBashコマンドプロンプトで利用できるツールです。BashUnix系OSで広く使われており,コマンドプロンプトWindows OSに標準で搭載されています。

今回はWindowsで作業を行っていますが,Bashを中心に説明します。Windows環境でもGit BashなどをインストールすることでBashを使うことができます。もしBashをインストールしていない場合は,Google Cloud ConsoleのCloud Shellを使って作業を行うことも可能です。

Google Cloud Consoleにアクセスし,右上のCloud Shellをアクティブにしてください。

Cloud Shell
すると,ターミナルが画面下部に表示されます。$マークの後にコマンドを入力していきます。

以下のコマンドをBashに入力して実行してください:

curl -X POST https://oauth2.googleapis.com/token \
  -d code=YOUR_AUTHORIZATION_CODE \
  -d client_id=YOUR_CLIENT_ID \
  -d client_secret=YOUR_CLIENT_SECRET \
  -d redirect_uri=YOUR_REDIRECT_URI \
  -d grant_type=authorization_code
  • YOUR_AUTHORIZATION_CODE 先ほど取得した認可コード
  • YOUR_CLIENT_ID クライアント ID
  • YOUR_CLIENT_SECRET クライアントシークレット
  • YOUR_REDIRECT_URI  リダイレクトURI
    を変更してください。

するとこのようなJSON形式でアクセストークンとリフレッシュトークンが返されます。

{
  "access_token": "ya29.***",
  "expires_in": 3599,
  "refresh_token": "***",
  "scope": "https://www.googleapis.com/auth/spreadsheets",
  "token_type": "Bearer"
}
Apps Script API の呼び出し

最後に,このトークンを使用してApps Script APIを呼び出します。

Apps Script APIのscriptsリソース ここでは,外部からGAS関数を実行したいので,Apps Script APIのscriptsリソースを使用します。リクエストは以下のようになっています。
HTTP リクエストの形式:

  • HTTP メソッド: POST
  • リクエスURI: https://script.googleapis.com/v1/scripts/{scriptId}:run

リクエストボディの構成
リクエストボディには次のようなJSON形式でデータを記述します:

{
  "function": string,
  "parameters": [
    value
  ],
  "sessionState": string,
  "devMode": boolean
}

getCellValue関数を実行したい場合

curl -X POST \
  -H "Authorization: Bearer access_token" \
  -H "Content-Type: application/json" \
  -d '{"function": "getCellValue"}' \
  https://script.googleapis.com/v1/scripts/***/run

このコマンドの説明は以下の通りです:

-X POST: POSTリクエストを送信します。

  • -H "Authorization: Bearer access_token": リクエストヘッダーにアクセストークンを指定します(access_token は実際のアクセストークンに置き換えてください)。
  • -H "Content-Type: application/json": リクエストボディのデータ形式JSONと指定します。
  • -d '...': リクエストボディのデータを指定します。ここでは実行したいGAS関数をJSON形式で指定しています。
  • https://script.googleapis.com/v1/scripts/***/run: リクエストを送信するAPIのエンドポイントです(実行可能API)。

このコマンドを実行すると やはりOAuth 2.0 Playgroundの時と同じようになります。

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.apps.script.v1.ExecutionResponse",
    "result": false
  }
}

resultがGoogle SheetのB2の値になっていることになります。 Google Sheetの値をTRUEに変えて同じコマンドで実行してみると

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.apps.script.v1.ExecutionResponse",
    "result": true
  }
}

resultがTRUEにになることが確認できました。

このようにして,CUI環境でも簡単にAPIを叩くことができます。レスポンスとしてJSONデータが返ってくるので,そこで結果を確認することが大切です。

setCellValue関数を実行したい場合

curl -X POST \
  -H "Authorization: Bearer access_token" \
  -H "Content-Type: application/json" \
  -d '{"function": "setCellValue","parameters": ["TRUE"]}' \
  https://script.googleapis.com/v1/scripts/***/run

setCellValue関数ではパラメータを受け取る仕様にしていたので,Google Sheetのセルの値をTRUEにしたいときパラメータ "parameters": ["TRUE"]とし,FALSEにしたいときは"parameters": ["FALSE"]にします。

アクセストークンの更新
curl -X POST https://oauth2.googleapis.com/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "refresh_token=YOUR_REFRESH_TOKEN" \
  -d "grant_type=refresh_token"

アクセストークンの更新についてのリクエストは,次のように実行します。YOUR_CLIENT_IDやYOUR_CLIENT_SECRET,YOUR_REFRESH_TOKENを置き換えてください。

まとめ

今回は,OAuth 2.0を使って認可コードを取得し,そのアクセストークンを使用してAPIを呼び出すテストの流れを見ていきました。次回は,ESP32を使って実際にAPIを叩き,返ってきたresultデータをもとにプログラムを動かしていく方法について説明します。

参考