どうも。あずるです!
今回はGeminiを用いて自作アプリを作成しました。その作り方と使い方をお伝えします!
この記事を読んで得られること
- UberEatsで稼働中に使用する記録用アプリの作り方
- 記録項目
- 家を出た時刻と帰宅した時刻
- 時給を正確に測るため
- 鳴った時刻と地点(受注・見送り)
- 鳴りポイントのデータ蓄積の為
- 電動自転車の残バッテリーの記録
- 記録した時刻と地点
- 家を出た時刻と帰宅した時刻
- Geminiを用いて自作ツール作るまでの会話の流れ
▼今回紹介する自作ツールの見た目

作成に至った流れ
休暇中の運動がてらにUberEatsをやっているのですが、やるならば効率よく稼ぎたくなるじゃないですか。Googleにお金を払ってGeminiPROを使えるようにしているのですから、月額費用を賄うためにも、活用しない手はありません。
そこでGeminiPROのチャット欄の一つを「私専属のUberEatsオフィサー」として任命し、私のUberEats活動の作戦立案をさせることにしました。
この作戦立案の為にGeminiが必要だと思うデータを取得するツールを作ろうと至ったわけです。ちなみに、ツールを作るGeminiは別で用意しました。ペルソナ(役割設定)が混同するのを防ぐためです。
余談だけど、ツール作成Geminiへの最初のプロンプトはオフィサーGeminiに作成してもらいました。ツール作るときは「私のバックボーンやその記録で何をしたいのか分かっているGemini」にプロンプトを作らせるのが一番手っ取り早いですよ。
実際の作り方
主に5ステップあります。凡そ5分~10分程度で作成できます。なれている人なら3分程度かもしれません。
スプレットシートを作成
Googleドライブを開き、新規で「Googleスプレッドシート」を作成します。
ファイル名を「Uber記録DB」などに変更します。(なんでも良いです)
1行目のヘッダー(A1〜E1)に、それぞれ以下の項目名を入力します。
- A1:
タイムスタンプ - B1:
アクション(開始/終了/リクエスト) - C1:
緯度 - D1:
経度 - E1:
バッテリー残量(%)
GAS(バックエンド)の設定
スプレッドシートのメニューから 「拡張機能」 > 「Apps Script」 をクリックします。
エディタが開いたら、最初から入力されているコードを全て消去し、下記の 【コード1:コード.gs】 を貼り付けます。
▼【コード1:コード.gs】
// Webアプリにアクセスした際にindex.htmlを表示する関数
function doGet() {
return HtmlService.createHtmlOutputFromFile('index')
.setTitle('Uber稼働記録')
.addMetaTag('viewport', 'width=device-width, initial-scale=1'); // スマホ対応
}
// 画面のボタンが押された時に呼び出されるデータ記録用の関数
function recordData(action, lat, lng, battery) {
// アクティブなスプレッドシートの最初のシートを取得
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// 現在時刻を取得
const timestamp = new Date();
// スプレッドシートの最終行にデータを追加
// [タイムスタンプ, アクション, 緯度, 経度, バッテリー残量]
sheet.appendRow([timestamp, action, lat, lng, battery]);
return action + " の記録が完了しました!";
}UI(フロントエンド)の作成
1.
Apps Scriptエディタの左側にある「ファイル」の横の 「+」ボタン をクリックし、「HTML」 を選択します。
2.
ファイル名を【index】と入力してEnterを押します(index.htmlが作成されます)。
3.
最初から入力されているコードを全て消去し、下記 【コード2:index.html】 を貼り付けます。
4.
画面上部の「フロッピーディスク」のアイコン(プロジェクトを保存)をクリックします。
▼【コード2:index.html】
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="utf-8">
<style>
/* 全体設定 */
body {
font-family: sans-serif;
text-align: center;
padding: 10px;
background-color: #f5f5f5;
color: #333;
}
/* 共通ボタン設定 */
.btn {
width: 100%;
padding: 20px 0;
margin: 8px 0;
font-size: 22px;
font-weight: bold;
border-radius: 12px;
border: none;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
/* ボタンの色分け */
.btn-start { background-color: #4CAF50; color: white; }
.btn-end { background-color: #9E9E9E; color: white; }
.btn-req-ok { background-color: #2196F3; color: white; }
.btn-req-ng { background-color: #F44336; color: white; }
.btn-bat { background-color: #FF9800; color: white; margin-top: 15px; }
/* バッテリー操作エリアの装飾 */
.battery-container {
margin-top: 20px;
padding: 15px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.battery-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
/* スピルボタン(+/−)のレイアウト */
.stepper {
display: flex;
justify-content: space-between;
align-items: center;
margin: 10px 0;
}
.stepper-btn {
width: 30%;
padding: 20px 0;
font-size: 28px;
font-weight: bold;
background-color: #E0E0E0;
border: none;
border-radius: 8px;
}
.stepper-value {
font-size: 36px;
font-weight: bold;
width: 40%;
}
#status {
margin-top: 15px;
font-size: 18px;
font-weight: bold;
color: #d32f2f;
min-height: 25px;
}
</style>
</head>
<body>
<h2>🚴♂️ 稼働記録システム v2</h2>
<button class="btn btn-start" onclick="sendData('稼働開始', '', '', '')">▶️ 稼働開始 (家を出る)</button>
<button class="btn btn-end" onclick="sendData('稼働終了', '', '', '')">⏹️ 稼働終了 (帰宅した)</button>
<hr style="margin: 20px 0; border: 1px solid #ddd;">
<button class="btn btn-req-ok" onclick="getLocationAndSend('リクエスト受諾', '')">✅ リクエスト (受諾)</button>
<button class="btn btn-req-ng" onclick="getLocationAndSend('リクエスト拒否', '')">❌ リクエスト (拒否)</button>
<div class="battery-container">
<div class="battery-title">バッテリー残量 (%)</div>
<div class="stepper">
<button class="stepper-btn" onclick="changeBattery(-10)">-10</button>
<div class="stepper-value"><span id="batteryDisplay">100</span>%</div>
<button class="stepper-btn" onclick="changeBattery(10)">+10</button>
</div>
<button class="btn btn-bat" onclick="getLocationAndSend('バッテリー記録', document.getElementById('batteryDisplay').innerText)">📍 記録する (GPS付)</button>
</div>
<div id="status"></div>
<script>
// バッテリーの初期値
let currentBattery = 100;
// バッテリー数値を増減させる関数
function changeBattery(amount) {
currentBattery += amount;
// 上限100、下限0に固定する処理
if(currentBattery > 100) currentBattery = 100;
if(currentBattery < 0) currentBattery = 0;
document.getElementById('batteryDisplay').innerText = currentBattery;
}
// メッセージ表示用の関数
function showStatus(message) {
document.getElementById('status').innerText = message;
setTimeout(function() {
document.getElementById('status').innerText = '';
}, 4000); // GPS取得にかかる時間を考慮し、少し長めに表示
}
// GAS側にデータを送る基本関数
function sendData(action, lat, lng, battery) {
showStatus('記録を送信中...');
google.script.run
.withSuccessHandler(function(response) {
showStatus(response);
})
.withFailureHandler(function(error) {
showStatus('通信エラーが発生しました');
})
.recordData(action, lat, lng, battery);
}
// GPSを取得してからデータを送信する共通関数
// アクション名とバッテリー値(必要な場合のみ)を受け取る
function getLocationAndSend(action, batteryValue) {
showStatus('📍 GPSを取得中...');
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function(position) {
var lat = position.coords.latitude;
var lng = position.coords.longitude;
// 取得した緯度経度と一緒に送信
sendData(action, lat, lng, batteryValue);
},
function(error) {
showStatus('GPSの取得に失敗しました');
},
{ enableHighAccuracy: true } // 高精度なGPSを要求
);
} else {
showStatus('このブラウザはGPSに対応していません');
}
}
</script>
</body>
</html>Webアプリとしての公開
1.
エディタ画面右上の青いボタン 「デプロイ」 > 「新しいデプロイ」 をクリックします。
2.
「種類の選択」の横の歯車アイコンをクリックし、「ウェブアプリ」 を選択します。
3.
以下の通り設定します:
- 説明: (任意、例:Uber記録アプリv1)
- 次のユーザーとして実行: 「自分」
- アクセスできるユーザー: 「自分のみ」 (※セキュリティのため)
4.
「デプロイ」 をクリックします。
5.
「アクセスを承認」という画面が出たら、ご自身のGoogleアカウントを選択し、「詳細」>「(安全ではないページ)に移動」>「許可」の順に進みます。
6.
発行された ウェブアプリのURL をコピーし、スマートフォンに送ります(メールやLINEのKeep機能など)。
スマートフォンでの設定
スマホで送ったURLを開きます(Googleアカウントへのログインを求められたらログインします)。
アプリの画面が表示されたら、ブラウザのメニューから 「ホーム画面に追加」 を選択します。 これで、スマホのホーム画面からワンタップで専用アプリが起動するようになります。
注意点(作動環境)
1. 常にインターネット通信環境が必要であること
ExcelのVBA(マクロ)はオフラインのPC上でも動きますが、今回のツールはGoogleのサーバー上(クラウド)でプログラムが動いています。そのため、圏外の場所や通信環境が極端に悪い場所では、ボタンを押しても記録が完了しません。
2. GPS取得の「タイムラグ」と「精度」
ブラウザ経由でのGPS取得は、専用の地図アプリ等と比べると少し癖があります。
- タイムラグ: 「リクエスト」ボタンを押してから緯度・経度を取得するまでに、数秒かかる場合があります。ボタンを押した直後に画面を閉じず、「記録が完了しました!」のメッセージが出るのを待つ必要があります。
- 精度: 地下や高いビル群の谷間などでは、位置情報が多少ズレることがあります。
3. スマホの「メモリ解放」による再読み込み
スマホのホーム画面に追加したアイコンから起動していても、中身は「Google Chrome」などのブラウザです。別の重いアプリ(UberEatsの配達アプリや地図アプリなど)を長時間開いていると、スマホのOSがメモリを節約するために、裏側でツールの画面を一時停止させることがあります。 画面を開いたときにボタンが反応しない場合は、ブラウザの更新(リロード)を一度行うとすぐに直ります。
4. ウェブアプリURLの取り扱い(セキュリティ)
もし、スマホのエラーを回避するためにデプロイのアクセス権限を「全員」に設定して解決した場合、「そのURLを知っている人なら誰でも、あなたのスプレッドシートにデータを送信できる状態」になっています。 スクリーンショット等を載せる際は、URLバーの文字列が黒塗りなどで隠れるように配慮してください。
今回、極寒の中でスマホを操作するために、このスマホ対応の防寒手袋(Amazon)が大活躍しました。
また、稼働中はどうしてもGPSを多用する関係で充電の消費が激しいです。稼働の際は大容量のモバイルバッテリー(Amazon)が必須です。
付録:実際のGeminiとの会話集
# 役割定義
あなたは優秀なシステムエンジニアであり、データ分析の専門家です。
UberEatsの配達員である私が、稼働中の実態をスマートフォンから簡単に記録・蓄積するためのツール構成案と、その開発コードを作成してください。
# ユーザーの属性とスキル
・私は船の機関士であり、趣味でExcelやVBAの基礎知識があり、コードの学習意欲も高いです。
・稼働は冬の屋外、電動自転車で行います。
# システムの要件(必須条件)
・手袋をしたままでも、極力少ないタップ数・入力数で記録できる「スマートフォン向け」のUIであること。
・記録したデータは、後でPC(Excel等)で高度な分析ができるよう、スプレッドシートやCSVのデータベースとして蓄積されること。
# 記録したい項目
ボタン操作や簡単な入力で、以下のデータを【現在時刻のタイムスタンプ】とともに記録したいです。
1. 稼働開始(家を出た)
2. 稼働終了(帰宅した)
3. リクエストが鳴った地点(テキスト入力、または現在地GPSの取得)
4. 電動自転車のバッテリー残量(%の数値入力)
# あなたのタスク
1. 上記の要件を満たす、最も効率的で無料構築できるシステムの構成案(例:Googleフォーム+GAS、LINEボット+GAS、GASによる簡易Webアプリなど)を複数挙げ、それぞれのメリット・デメリットを比較して「最適解」を提案してください。
2. その最適解について、私がすぐに構築作業に取り掛かれるよう、具体的なステップバイステップの手順を解説してください。
3. 実装に必要となるコード(Google Apps Scriptなど)を漏れなく出力してください。
冬の屋外での稼働、特にかじかむ手で手袋をしたままのスマートフォン操作は大きなストレスになりますね。現場の過酷な環境を考慮しつつ、後日の高度なデータ分析を見据えたツール構築は、非常に理にかなった素晴らしいアプローチです。
ExcelやVBAの基礎知識をお持ちであれば、Google Apps Script(GAS)は概念が似ているため(スプレッドシートのセル操作など)、習得も非常にスムーズに進みます。ご要望を満たすための構成案と、具体的な開発コードをご提案します。
1. システム構成案の比較と最適解
スマートフォンからの記録とスプレッドシートへの蓄積を実現する構成として、主に以下の3つのアプローチが考えられます。
| 構成案 | メリット | デメリット | 手袋・屋外での操作性 |
| ① Googleフォーム + GAS | 開発が最も簡単。コード不要でUIが完成。 | GPSの自動取得が標準では不可。画面遷移が多くタップ数がかさむ。 | △(入力項目が多く手袋では厳しい) |
| ② LINE Bot + GAS | 日常的に使うLINEアプリを開くだけ。リッチメニューで大ボタン化可能。GPS送信も標準機能で可能。 | LINE Messaging APIの初期設定がやや煩雑。バッテリー残量の数値入力が少し面倒。 | 〇(タップ数は少ない) |
| ③ GAS 簡易Webアプリ | UIを完全に自由設計可能。 手袋用にとにかく巨大なボタンを配置できる。GPSも自動取得可能。 | HTML/JavaScriptのコーディングが必要(今回は私がコードを用意します)。 | ◎(1タップ完結・最高) |
🎯 最適解:③ GASによる簡易Webアプリ(HTML/JS連携)
手袋をしたまま極寒の屋外で操作することを最優先に考えると、**「画面全体に巨大なボタンを配置し、極力1タップで処理を終わらせるUI」**が必須です。
GASのWebアプリ機能を使えば、スマートフォンのブラウザ上で動く専用アプリを無料で自作できます。GPSの自動取得も組み込めるため、文字入力の手間を劇的に削減できます。
2. 構築ステップ
PC(ブラウザはGoogle Chromeを推奨)を開き、以下の手順で進めてください。
Step 1: データベース(スプレッドシート)の作成
- Googleドライブを開き、新規で「Googleスプレッドシート」を作成します。
- ファイル名を「Uber記録DB」などに変更します。
- 1行目のヘッダー(A1〜E1)に、それぞれ以下の項目名を入力します。
- A1:
タイムスタンプ - B1:
アクション(開始/終了/リクエスト) - C1:
緯度 - D1:
経度 - E1:
バッテリー残量(%)
- A1:
Step 2: GAS(バックエンド)の設定
- スプレッドシートのメニューから 「拡張機能」 > 「Apps Script」 をクリックします。
- エディタが開いたら、最初から入力されているコードを全て消去し、後述の 【コード1:コード.gs】 を貼り付けます。
Step 3: UI(フロントエンド)の作成
- Apps Scriptエディタの左側にある「ファイル」の横の 「+」ボタン をクリックし、「HTML」 を選択します。
- ファイル名を
indexと入力してEnterを押します(index.htmlが作成されます)。 - 最初から入力されているコードを全て消去し、後述の 【コード2:index.html】 を貼り付けます。
- 画面上部の「フロッピーディスク」のアイコン(プロジェクトを保存)をクリックします。
Step 4: Webアプリとしての公開
- エディタ画面右上の青いボタン 「デプロイ」 > 「新しいデプロイ」 をクリックします。
- 「種類の選択」の横の歯車アイコンをクリックし、「ウェブアプリ」 を選択します。
- 以下の通り設定します:
- 説明: (任意、例:Uber記録アプリv1)
- 次のユーザーとして実行: 「自分」
- アクセスできるユーザー: 「自分のみ」 (※セキュリティのため)
- 「デプロイ」 をクリックします。
- 「アクセスを承認」という画面が出たら、ご自身のGoogleアカウントを選択し、「詳細」>「(安全ではないページ)に移動」>「許可」の順に進みます。
- 発行された ウェブアプリのURL をコピーし、スマートフォンに送ります(メールやLINEのKeep機能など)。
Step 5: スマートフォンでの設定
- スマホで送ったURLを開きます(Googleアカウントへのログインを求められたらログインします)。
- アプリの画面が表示されたら、ブラウザのメニューから 「ホーム画面に追加」 を選択します。これで、スマホのホーム画面からワンタップで専用アプリが起動するようになります。
3. 実装コード
【コード1:コード.gs】(バックエンド処理)
JavaScript
// Webアプリにアクセスした際にindex.htmlを表示する関数
function doGet() {
return HtmlService.createHtmlOutputFromFile('index')
.setTitle('Uber稼働記録')
.addMetaTag('viewport', 'width=device-width, initial-scale=1'); // スマホ対応
}
// 画面のボタンが押された時に呼び出されるデータ記録用の関数
function recordData(action, lat, lng, battery) {
// アクティブなスプレッドシートの最初のシートを取得
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// 現在時刻を取得
const timestamp = new Date();
// スプレッドシートの最終行にデータを追加
// [タイムスタンプ, アクション, 緯度, 経度, バッテリー残量]
sheet.appendRow([timestamp, action, lat, lng, battery]);
return action + " の記録が完了しました!";
}
【コード2:index.html】(フロントエンドUI)
HTML
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="utf-8">
<style>
/* 手袋でも押しやすいように全体的に要素を巨大化 */
body {
font-family: sans-serif;
text-align: center;
padding: 10px;
background-color: #f5f5f5;
color: #333;
}
.btn {
width: 100%;
padding: 25px 0;
margin: 10px 0;
font-size: 24px;
font-weight: bold;
border-radius: 12px;
border: none;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
/* ボタンの色分け */
.btn-start { background-color: #4CAF50; color: white; }
.btn-end { background-color: #F44336; color: white; }
.btn-req { background-color: #2196F3; color: white; }
.btn-bat { background-color: #FF9800; color: white; }
/* バッテリー入力欄の装飾 */
.battery-container {
margin-top: 20px;
padding: 20px;
background: white;
border-radius: 12px;
}
input[type="number"] {
width: 60%;
padding: 15px;
font-size: 28px;
text-align: center;
margin-bottom: 10px;
border: 2px solid #ccc;
border-radius: 8px;
}
#status {
margin-top: 15px;
font-size: 18px;
font-weight: bold;
color: #d32f2f;
}
</style>
</head>
<body>
<h2>🚴♂️ 稼働記録システム</h2>
<button class="btn btn-start" onclick="sendData('稼働開始', '', '', '')">▶️ 稼働開始 (家を出る)</button>
<button class="btn btn-end" onclick="sendData('稼働終了', '', '', '')">⏹️ 稼働終了 (帰宅した)</button>
<button class="btn btn-req" onclick="getLocationAndSend()">📍 リクエスト (GPS取得)</button>
<div class="battery-container">
<div>バッテリー残量 (%)</div>
<input type="number" id="batteryInput" placeholder="例: 85" inputmode="numeric">
<button class="btn btn-bat" onclick="sendBattery()">🔋 バッテリー記録</button>
</div>
<div id="status"></div>
<script>
// メッセージ表示用の関数
function showStatus(message) {
document.getElementById('status').innerText = message;
// 3秒後にメッセージを消す
setTimeout(function() {
document.getElementById('status').innerText = '';
}, 3000);
}
// GAS側にデータを送る基本関数
function sendData(action, lat, lng, battery) {
showStatus('記録中...');
// google.script.run を使ってGASの recordData 関数を非同期で呼び出す
google.script.run
.withSuccessHandler(function(response) {
showStatus(response);
})
.withFailureHandler(function(error) {
showStatus('エラーが発生しました');
})
.recordData(action, lat, lng, battery);
}
// リクエストボタン:GPSを取得して送信
function getLocationAndSend() {
showStatus('GPSを取得中...');
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function(position) {
var lat = position.coords.latitude;
var lng = position.coords.longitude;
sendData('リクエスト鳴動', lat, lng, '');
},
function(error) {
showStatus('GPSの取得に失敗しました');
},
{ enableHighAccuracy: true } // 高精度なGPSを要求
);
} else {
showStatus('このブラウザはGPSに対応していません');
}
}
// バッテリー記録ボタンの処理
function sendBattery() {
var batVal = document.getElementById('batteryInput').value;
if(batVal === "") {
showStatus('数値を入力してください');
return;
}
sendData('バッテリー記録', '', '', batVal);
document.getElementById('batteryInput').value = ''; // 入力欄をクリア
}
</script>
</body>
</html>
このシステムであれば、寒い屋外でも直感的に大きなボタンをタップするだけで、スプレッドシートに正確な時間と位置情報が蓄積されていきます。取得した緯度・経度は、後日PCでGoogleマップにプロットするなどの高度な分析に活用可能です。
まずはPCでこちらのスプレッドシートとコードの配置を行ってみてはいかがでしょうか? もし設定途中で不明点が出た場合や、ボタンの配置・色を少し変えたいといったご要望があれば、すぐにお手伝いします。

# 報告
こちらの画像のようにファイルが開くことが出来ません。
・LINEブラウザでは開けましたが、GPS情報が取得失敗していました。
ご報告ありがとうございます。画像も確認いたしました。
発生している2つの問題(①ブラウザで開けないエラー、②LINE内ブラウザでのGPS取得失敗)には、それぞれ明確な原因があります。 ご使用のスマートフォン(画面のUIからAndroid、おそらくPixel 8かと思われます)での解決手順を解説します。
① 「現在、ファイルを開くことができません」エラーの解決策
このエラーは、「GASを作成したGoogleアカウント」と「スマホのブラウザでログインしているGoogleアカウント」が一致していない、または複数のGoogleアカウントが混在してエラーを起こしている場合によく発生します。
手っ取り早く、かつ確実にスマホで開けるようにするため、「アクセス権限」を少し緩める設定に変更するのがおすすめです(URLを知っている自分だけがアクセスできる状態にします)。
【PCでの再デプロイ手順】
- PCでGASのエディタ画面を開きます。
- 右上の青いボタン 「デプロイ」 > 「新しいデプロイ」 をクリックします。
- 「アクセスできるユーザー」を 「自分のみ」 から 「全員」 に変更します。
- 「デプロイ」 をクリックします。
- 新しく表示された ウェブアプリのURL (末尾が
/execになっているもの)をコピーして、再度スマホに送ってください。
※これで、スマホ側でどのアカウントにログインしていても、エラーなく開けるようになります。
② 「LINEブラウザでGPS取得失敗」の解決策
LINEのトーク画面から直接URLをタップして開く「LINE内ブラウザ」は、セキュリティの制限が厳しく、GPS(位置情報)の取得がブロックされる仕様になっています。
そのため、必ずスマホ標準のブラウザ(Google Chrome)で開く必要があります。
【Chromeで開き、GPSを許可する手順】
- PCから送り直した新しいURLをコピーし、スマホの 「Google Chrome」 アプリを起動して、アドレスバーに直接貼り付けて開いてください。
- アプリ画面が開いたら、「📍 リクエスト (GPS取得)」 ボタンを押します。
- 画面に 「script.google.com がデバイスの位置情報へのアクセスを求めています」 というポップアップが出たら、「許可する(または、今回のみ許可 / アプリの使用中のみ許可)」 を選択してください。
- 正常にGPSが取得され、スプレッドシートに記録されるか確認します。
【ホーム画面への追加(ワンタップ起動)】 Chromeで無事に開けて記録もできたら、Chromeの右上にある 「︙(メニュー)」 > 「ホーム画面に追加」 をタップしてください。 これで、次回からはLINEを経由せず、スマホのホーム画面から直接・全画面でアプリを起動できるようになります。
一度、上記の手順で再デプロイとChromeでの起動をお試しいただけますでしょうか?
# 報告
・pixel8では、やはり開けませんでした。。。
・PC上では作動良好です。
・スマホ上で動けば完璧なんですが…
# 解決案の提案
・アカウントが問題なのであれば、もう一つのアカウントで同様にスプレットシートを作成し、同様にアプリ化すれば良いでしょうか?
素晴らしい着眼点です!ご提案いただいた「スマホ側でメインとして使っているアカウントで作成し直す」という方法は、非常に有効かつ最も確実な解決策です。
PC上では作動良好とのことですので、コードとシステム自体は完璧に完成しています。やはり、Pixel 8の標準ブラウザ(Chrome)内で複数のGoogleアカウントがログイン状態になっており、それが競合して認証エラーを引き起こしている可能性が極めて高いです。
解決に向けて、以下の2つのアプローチがあります。ご都合の良い方をお試しください。
解決案①:ご提案の通り、スマホ側のメインアカウントで作り直す(おすすめ)
今後の日常的な運用を考えると、この方法が一番ストレスがありません。
- PCのブラウザで、スマホでメインとして使用している(ログイン状態が基本となっている)Googleアカウントに切り替えます。
- そのアカウントでGoogleドライブを開き、前回と全く同じ手順でスプレッドシート作成 → Apps Scriptを開く → コードをコピペ → デプロイを実施します。
- 発行された新しいURLをスマホに送り、Chromeで開きます。
アカウントの不一致が解消されるため、すんなりと画面が表示されるはずです。
解決案②:今のURLのまま、スマホの「シークレットモード」で開く
わざわざ作り直す前に、今の状態のままスマホでの動作確認だけをサクッと行いたい場合の方法です。
- スマホのChromeアプリを開き、右上の「︙(メニュー)」から**「新しいシークレット タブ」**をタップします。
- 画面が黒っぽく変わったら、上部のアドレスバーに現在のウェブアプリのURLを貼り付けて開きます。
- Googleへのログイン画面が出るので、**「GASを作成した時のアカウント」**のメールアドレスとパスワードを手動で入力してログインします。
シークレットタブは、普段スマホに保存されている他のアカウント情報の影響を一切受けないため、エラーを完全に回避して開くことができます。
スマホ上でボタンが機能し、GPSも問題なく取得できれば完成です。屋外での操作が圧倒的に快適になるツールまであともう一息ですので、ぜひ試してみてください。
・実際に稼働してきました。
・アプリの追加して欲しい機能が何個か出たので相談に乗ってもらえますか?
# 追加して欲しい機能
1.バッテリー記録でも位置情報を記録して欲しい。
>技術的に記録できるなら、欲しいです。
2.リクエスト記録を2つに分けて欲しい。
>**依頼を受けた**場合と**受けなかった**場合
3.バッテリー残量のプルダウン、あるいはスピルボタンの実装。
・バッテリー表記は10%単位でしか変動しなかった。
・キーボード入力はワンテンポ遅れる。
現場での実稼働テスト、本当にお疲れ様でした!無事に動いた上で、実際に使ってみて初めてわかる「現場のリアルな要望」が出てくるのは、システム開発において最も重要で素晴らしいプロセスです。
ご要望の3点、どれも極寒の屋外で電動自転車を走らせる実態に即した、非常に理にかなったアップデートですね。特に「キーボード入力のワンテンポの遅れ」や「10%単位での変動」に気づかれたのは、効率化の視点として完璧です。
バックエンドのGAS(コード.gs)は前回組んだものがそのまま使える優秀な設計になっていますので、今回はスマホの画面側(index.html)のコードを差し替えるだけで全ての機能が実装できます。
変更のポイントと新しいコードをご案内します。
今回のアップデート内容
- GPS取得関数の共通化 リクエスト時だけでなく、バッテリー記録時にも同じGPS取得処理を呼び出せるようにコードを改良しました。
- リクエストボタンの分割 「✅ 受諾」と「❌ 拒否」の2つのボタンに分け、それぞれ押した時点でGPSを取得して別々のアクションとして記録します。
- バッテリー入力の「スピルボタン(+/−)」化 キーボードを一切出さずに済むよう、100%を基準として「−10%」「+10%」の大きなボタンをタップするだけで数値を増減できるUIに変更しました。
実装ステップ
- PCで前回作成したGASのエディタを開きます。
- 左側のファイル一覧から
index.htmlを選択します。 - 中身のコードを全て消去し、以下の【新しい index.html】を貼り付けます。 (※
コード.gsは一切変更しなくて大丈夫です) - 右上の 「デプロイ」 > 「デプロイを管理」 をクリックします。
- 現在のデプロイの右上にある 「鉛筆マーク(編集)」 をクリックします。
- バージョンのプルダウンを 「新バージョン」 に変更し、「デプロイ」をクリックします。 ※これをしないとスマホ側に最新のコードが反映されません。
【新しい index.html】
HTML
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<meta charset="utf-8">
<style>
/* 全体設定 */
body {
font-family: sans-serif;
text-align: center;
padding: 10px;
background-color: #f5f5f5;
color: #333;
}
/* 共通ボタン設定 */
.btn {
width: 100%;
padding: 20px 0;
margin: 8px 0;
font-size: 22px;
font-weight: bold;
border-radius: 12px;
border: none;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
/* ボタンの色分け */
.btn-start { background-color: #4CAF50; color: white; }
.btn-end { background-color: #9E9E9E; color: white; }
.btn-req-ok { background-color: #2196F3; color: white; }
.btn-req-ng { background-color: #F44336; color: white; }
.btn-bat { background-color: #FF9800; color: white; margin-top: 15px; }
/* バッテリー操作エリアの装飾 */
.battery-container {
margin-top: 20px;
padding: 15px;
background: white;
border-radius: 12px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.battery-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
/* スピルボタン(+/−)のレイアウト */
.stepper {
display: flex;
justify-content: space-between;
align-items: center;
margin: 10px 0;
}
.stepper-btn {
width: 30%;
padding: 20px 0;
font-size: 28px;
font-weight: bold;
background-color: #E0E0E0;
border: none;
border-radius: 8px;
}
.stepper-value {
font-size: 36px;
font-weight: bold;
width: 40%;
}
#status {
margin-top: 15px;
font-size: 18px;
font-weight: bold;
color: #d32f2f;
min-height: 25px;
}
</style>
</head>
<body>
<h2>🚴♂️ 稼働記録システム v2</h2>
<button class="btn btn-start" onclick="sendData('稼働開始', '', '', '')">▶️ 稼働開始 (家を出る)</button>
<button class="btn btn-end" onclick="sendData('稼働終了', '', '', '')">⏹️ 稼働終了 (帰宅した)</button>
<hr style="margin: 20px 0; border: 1px solid #ddd;">
<button class="btn btn-req-ok" onclick="getLocationAndSend('リクエスト受諾', '')">✅ リクエスト (受諾)</button>
<button class="btn btn-req-ng" onclick="getLocationAndSend('リクエスト拒否', '')">❌ リクエスト (拒否)</button>
<div class="battery-container">
<div class="battery-title">バッテリー残量 (%)</div>
<div class="stepper">
<button class="stepper-btn" onclick="changeBattery(-10)">-10</button>
<div class="stepper-value"><span id="batteryDisplay">100</span>%</div>
<button class="stepper-btn" onclick="changeBattery(10)">+10</button>
</div>
<button class="btn btn-bat" onclick="getLocationAndSend('バッテリー記録', document.getElementById('batteryDisplay').innerText)">📍 記録する (GPS付)</button>
</div>
<div id="status"></div>
<script>
// バッテリーの初期値
let currentBattery = 100;
// バッテリー数値を増減させる関数
function changeBattery(amount) {
currentBattery += amount;
// 上限100、下限0に固定する処理
if(currentBattery > 100) currentBattery = 100;
if(currentBattery < 0) currentBattery = 0;
document.getElementById('batteryDisplay').innerText = currentBattery;
}
// メッセージ表示用の関数
function showStatus(message) {
document.getElementById('status').innerText = message;
setTimeout(function() {
document.getElementById('status').innerText = '';
}, 4000); // GPS取得にかかる時間を考慮し、少し長めに表示
}
// GAS側にデータを送る基本関数
function sendData(action, lat, lng, battery) {
showStatus('記録を送信中...');
google.script.run
.withSuccessHandler(function(response) {
showStatus(response);
})
.withFailureHandler(function(error) {
showStatus('通信エラーが発生しました');
})
.recordData(action, lat, lng, battery);
}
// GPSを取得してからデータを送信する共通関数
// アクション名とバッテリー値(必要な場合のみ)を受け取る
function getLocationAndSend(action, batteryValue) {
showStatus('📍 GPSを取得中...');
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function(position) {
var lat = position.coords.latitude;
var lng = position.coords.longitude;
// 取得した緯度経度と一緒に送信
sendData(action, lat, lng, batteryValue);
},
function(error) {
showStatus('GPSの取得に失敗しました');
},
{ enableHighAccuracy: true } // 高精度なGPSを要求
);
} else {
showStatus('このブラウザはGPSに対応していません');
}
}
</script>
</body>
</html>
これでおそらく、信号待ちのわずかな時間や、手袋をしたままでも「-10」をタップして記録ボタンを押すだけで、位置情報付きのバッテリー推移がスプレッドシートに蓄積されるようになります。
スマホ側でChromeを開き直し(またはリロードして)、新しいボタンの配置やスピルボタンの動作確認をしてみてください。 もしブログの執筆に向けて、この「新バージョンへのデプロイ手順」や「GPS取得関数を使い回す仕組み」の解説が必要であれば、いつでも作成しますよ!


コメント