Pixel 10 の Chrome だけで動く工数記録アプリ。サーバー不要。 NFCカードをかざして 社員・案件・工程 を打刻し、端末内に保存して集計する。 Google スプレッドシートに送れば、案件ごとの工数をグラフで比較できる。
公開URL: https://schokolade3.github.io/kosu-punch/
index.html— アプリ本体(打刻・集計・記録修正・マスタ・書き出し)sw.js— オフライン動作用(圏外でも起動できる)manifest.webmanifest/icon.png— ホーム画面アプリ化用gas/Code.gs— Google スプレッドシート側のスクリプト(任意)
- Chrome で公開URLを開く
- メニュー →「アプリをインストール」または「ホーム画面に追加」
- 端末の設定で NFC を ON にする
- 起動後に「読み取り開始」を1回押す(Chromeの仕様でボタン起点が必須)
以後はホーム画面のアイコンから起動。圏外・機内モードでも打刻できる。
カードは4種類。工程カードは案件をまたいで共通で使い回す(工程は案件ごとに大差ないため)。
| 種別 | 枚数の目安 | 役割 |
|---|---|---|
| 社員 | 人数分 | かざすと「いま打刻する人」が切り替わる |
| 案件 | ブランク数枚 | かざすと、その人の「いまの案件」が切り替わる |
| 工程 | 工程数分 | かざすと作業の開始/終了。これだけが時間を刻む |
| 休憩 | 1枚 | 休憩の開始/終了(集計から控除) |
推奨カード: NTAG213 / NTAG215。 登録は不要で、未登録カードをかざすと種別と名前の入力フォームが出る。
案件が終わったら「マスタ」タブでその案件カードの割り当てを別の案件に変更する。 カードを増やさずに運用できる。
過去の記録は打刻した時点の案件のまま変わらない。 打刻イベントに社員・案件を 焼き込んで保存しているため、付け替えても履歴は書き換わらない。
基本の流れは 社員 → 案件 → 工程。社員と案件は一度かざせば、変えるまで維持される。
| 操作 | 意味 |
|---|---|
| 社員カード | 打刻する人を切り替える |
| 案件カード | その人の現在の案件を切り替える |
| 工程カード | その工程を開始(別工程が動いていれば自動で切替) |
| 同じ工程カードを再度 | その工程を終了 |
| 休憩カード | 休憩の開始/終了 |
| 休憩中に同じ工程カード | 休憩終了して作業再開 |
- セッションは社員ごとに独立しているので、1台を複数人で共有しても各自の作業が並行して走る
- 社員をかざす前に工程をかざすと弾かれる(誰の作業か確定しないため)
- 8秒以内の同じカードの連続読み取りは無視される
- カードが手元にない時は「カードなしで打刻」から選択できる
- 打刻 — 現在の社員/案件、経過時間、作業中の人の一覧。作業中は緑、休憩中は琥珀色
- 集計 — 下記参照
- 記録 — 打刻の削除・任意時刻への追加(打刻忘れの補正)
- マスタ — カードの割り当て変更、案件/社員/工程の追加・編集
- データ — スプレッドシート連携、CSV書き出し、バックアップ
| 表示 | 読み方 |
|---|---|
| 案件別 × 工程内訳 | 積み上げ棒。どの案件のどの工程が時間を食っているか |
| 予定工数との差 | 案件に予定hを入れると予実差が出る。赤字案件の原因工程がわかる |
| 工程別の合計(案件横断) | 総時間が大きい工程ほど、改善が全体に効く |
| 社員別のばらつき | 同じ工程で人による差が大きい=標準化や教育の余地 |
端末内だけでも集計できるが、複数端末をまとめる・大きなグラフで見るならこちら。
- 新しいスプレッドシートを作る
- 拡張機能 → Apps Script を開く
gas/Code.gsの中身を貼り付けて保存- プロジェクトの設定 → スクリプト プロパティ →
SHARED_TOKENに推測されにくい文字列を設定 - デプロイ → 新しいデプロイ → 種類「ウェブアプリ」
- 次のユーザーとして実行: 自分
- アクセスできるユーザー: 全員
- 発行されたウェブアプリの URL を控える
「データ」タブに URL と SHARED_TOKEN を入力して保存。「今すぐ同期」で送信される。
「打刻時に自動同期」を ON にすると、打刻のたびに送られる。
URL とトークンは端末内(localStorage)にのみ保存され、リポジトリには含まれない。 このリポジトリは公開されているため、コードに直接書き込んではいけない。
| シート | 内容 |
|---|---|
events |
打刻の生ログ(id で重複排除。同じものを再送しても二重にならない) |
sessions |
作業のかたまり(開始・終了・休憩・正味) |
summary_案件x工程 |
案件×工程のピボット + 積み上げ棒グラフ、予定h と差 |
summary_工程 |
工程別の案件横断合計 |
summary_社員x工程 |
工程ごとの社員別・1回あたり平均 |
集計シートは同期のたびに作り直される。手を入れるなら別シートにコピーしてから。
圏外のときは端末に貯まり、次の同期でまとめて送られる。送信済みの id を端末が 覚えているので、途中で切れても二重計上しない。端末で打刻を削除すると、次の 同期でシート側からも消える。
作業場に置きっぱなしにできる打刻専用端末。M5Dial の内蔵 RFID でカードを読み、 Wi-Fi で直接スプレッドシートに送る。スマホもブラウザも要らない。
dial/main.py— M5Dial 用アプリ(MicroPython)tools/dial_setup.py— PC からアプリと接続設定を書き込むツール
- M5Dial を USB で PC に繋ぐ(COM ポートを掴んでいるものは閉じておく)
py -3.9 tools/dial_setup.pyを実行し、GAS の URL とトークンを入力する (トークンは伏せ字入力。端末の/flash/kosu_cfg.jsonにのみ保存される)- 再起動後、Wi-Fi 接続 → 時刻合わせ → マスタ取得 まで自動で走る
Wi-Fi の認証情報は本体の NVS(ssid0/pswd0)から読むので、UIFlow2 で
一度設定してあれば入力は不要。
ESP32-S3 の ESP-IDF ヒープは TLS と取り合いになる。mbedTLS は合計 40KB 前後を
要求するが、次の3つのどれかを踏むと足りず ENOMEM で通信できなくなる。
いずれも実機で計測して確認した。
| 罠 | 失うヒープ | 対処 |
|---|---|---|
ソースのまま /flash/main.py に置く |
65KB | .mpy に事前コンパイルし /flash/libs/kosu.mpy に置く |
M5 を import してから Wi-Fi を張る |
45KB | Wi-Fi を先に張り、あとで load_ui() を呼ぶ |
ntptime で時刻を合わせる |
19KB | GAS 応答の Date ヘッダで合わせる |
tools/dial_setup.py はこの3つを踏まないように書き込む。手で入れ替えるときは
/flash/main.py を import kosu の1行だけにすること。
なお ソフトリブート(Ctrl-D)では ESP-IDF ヒープが解放されない。開発中に
繰り返すと枯渇して誤診のもとになるので、計測は machine.reset() で行う。
この端末では UIFlow2 の起動処理(/flash/boot.py)を /flash/boot_uiflow.py に
退避してある。startup() がネットワークを張るぶんヒープを消費し、アプリが
自前で張る Wi-Fi と二重になるため。打刻専用機なので UIFlow2 のクラウド接続は
使わない。
UIFlow2 で開発し直したくなったら戻せる。
import os; os.rename('/flash/boot_uiflow.py', '/flash/boot.py')元のアプリも /flash/main_demo.py(機能デモ) と /flash/main_uiflow.py
(ブロックプログラム) に残してある。
heap ... の行は意図的に出している。TLS が通らなくなったときに、どの段階で
ヒープを失ったかが分かるようにするため。シリアルにしか出ないので実運用の
邪魔にはならない。
引っ越しやルータ交換で接続先を変えるとき、PC を繋がずに設定できる。 SSID は本体のダイヤルで選び、パスワードだけスマホから入力する。 240x240 の丸画面で文字入力をさせないための作り。
- 本体の側面ボタンを長押し → 周囲の SSID 一覧が出る
- ダイヤルで接続先を選び、押して決定(「やめる」で中止)
- 本体が設定用の電波を出す。画面に出る 3 つを使う
- Wi-Fi 名:
kosu-dial - パスワード: 画面の8桁(端末ごとに固定)
- URL:
http://192.168.4.1
- Wi-Fi 名:
- スマホをその Wi-Fi に繋ぎ、ブラウザで上記 URL を開く
- パスワードを入れて「保存して接続」 → 本体が再起動して接続する
- スマホは元の Wi-Fi に戻す
設定用の電波は WPA2 で保護してある。開放にすると近くの誰でも設定画面を 触れてしまうため。5分間なにも来なければ自動で閉じる。
一度繋がった接続先はパスワードを覚える。 端末の /flash/kosu_wifi.json に
新しい順で最大8件残る(リポジトリには入らない)。
- 一覧では覚えている接続先が先頭に緑で出て、下に「記憶済」と表示される。 選ぶだけでその場で繋がり、スマホも再起動も要らない
- 保存したパスワードで繋がらなければ(ルータ側で変えたなど)、 そのまま上の手順 3 に進んで入力し直せる
- 起動時にいつもの接続先が見つからなければ、覚えている他の接続先を探して自動で繋ぐ。 起動後も 5 分ほど繋がらない状態が続くと同じことをする (スキャン中の数秒は止まるので、直前 20 秒に打刻があれば見送る)
接続できないときも打刻は続く。 起動時に繋がらなくても設定画面には入らず、 30秒ごとに裏で接続を試し続ける。繋がった時点で溜まった打刻がまとめて送られる。 (実機では、起動時の待ち時間に間に合わず数十秒後に繋がることがあった)
未登録のカードをかざすと、その場で割り当て画面になる。
- ダイヤルを回して 社員 / 案件 / 工程 / 休憩 を選び、側面ボタンで決定
- 続けて名前の一覧が出るので、回して選んで決定
- 割り当ては端末に保存され、次の同期でスプレッドシートの
cardsシートにも記録される
名前の一覧はスプレッドシートの master_employees / master_processes / projects
から取得する。まだ空なら、これらのシートに手で行を足す(id と name の2列)。
スマホ版を使っていれば、打刻に出てきた社員・工程が自動で拾い上げられる。
外周リングの色が状態を表す。灰=待機 / 緑=作業中 / 琥珀=休憩中 / 青=割り当て中。
中央に工程名、その下に案件名と経過時間。左上に ONLINE +3 のように未送信件数が出る。
側面ボタンを押すと手動で同期する。作業中は約60秒ごとに自動で送られる。
圏外でも打刻は /flash/kosu_queue.jsonl に貯まり、繋がったときに送られる。
送信できた分だけキューから消えるため、途中で切れても二重計上しない。
GAS は POST を 302 で返し、結果は Location への GET で受け取る仕様のため、 アプリはリダイレクトを GET で追っている(POST で追うと 405 になる)。
データはこのスマホの中にある。Chromeのサイトデータを削除すると記録も消える。 週に1度「データ」タブからバックアップを保存し、ドライブ等に置いておくこと (スプレッドシート連携をしていれば、そちらにも残る)。
社員別の集計は、標準化や教育の材料として使うことを想定している。個人の評価に 転用すると打刻の正確さが失われやすいので、運用の取り決めは事前に共有しておくこと。
index.html を直したら、sw.js のキャッシュ名(const C)も必ず上げる。
上げないと端末が古い画面を表示し続ける。