Alexa & IFTTT & GASを使って音声だけで勤怠管理

# はじめに
在宅勤務メインになると、その日にどれくらい仕事をしたのかが分からなくなりがちです。
仕事の時間ではなく成果で評価するべきだ、みたいな話もありますが、そうは言っても会社の勤怠は時間ベースでつけないといけないですし、実際どれくらい作業したのかを自分でも把握しておきたいです。
どうせならなるべく楽に記録したいので、Alexaを使って音声だけで記録するようにしてみました。

※以下の記事を参考にさせて頂いています。良記事ありがとうございます。
[声で記録する出退勤管理簿をたった10分でつくる方法](https://qiita.com/zono_0/items/dde0e00d803c4acd25ea)

# できること
以下が完成形のイメージです。
![できること.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114715/d87a98cb-9c82-54f7-f6c7-6eb82502b37b.png)
Alexaに仕事開始、休憩、再開、終了を呼びかけると、それぞれの日付時刻をGoogleスプレッドシートに記録します。さらに今のステータス(仕事中、休憩中など)や、日付別の仕事時間も保存します。
ステータスのシートを画面の片隅に置いたりスマホに表示しておくと、その日の仕事時間の合計などをさっと確認できて便利です。

この程度であれば、Alexaスキルを作らなくても色々な部品の組み合わせで実現できました。

# 仕組み
## 全体構成
全体構成は以下です。
![全体構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114715/95ef4b03-dce9-2d1b-7915-50592527efc2.png)
AlexaからIFTTT経由でGoogleスプレッドシートに時刻とアクション(「開始」「中断」など)を書き込んだのち、GAS(Google Apps Script)で集計を行う、という方針です。
GASにはトリガーを設定し、IFTTTからの書き込みに反応して自動的に集計が動作するようにします。

※ソース等は[github](https://github.com/uemuram/alexa-attendance)に置いています。

## 作り方

### IFTTT設定
IFTTTを経由して、スプレッドシートに時刻とアクションを書き込むまでを作成します。
[こちら](https://qiita.com/zono_0/items/dde0e00d803c4acd25ea)の方法で設定を行いますが、追加や変更が必要な内容を記載しておきます。

#### 複数アプレット作成
今回はアプレットを4つ作成します。それぞれのアプレットの設定は以下です。
書き込み先は全て同じスプレッドシートにしてください。

| トリガーフレーズ | Formatted row                       |Spreadsheet name    |Drive folder path |
|:-----------------|:------------------------------------|:-------------------|:-----------------|
| 仕事開始         |{{TriggeredAt}}&#124;&#124;&#124;開始|【スプレッドシート名】  |【配置フォルダパス】  |
| 休憩             |{{TriggeredAt}}&#124;&#124;&#124;中断|〃                  |〃                |
| 仕事再開         |{{TriggeredAt}}&#124;&#124;&#124;再開|〃                  |〃                |
| 仕事終わり       |{{TriggeredAt}}&#124;&#124;&#124;終了|〃                  |〃                |
なお、トリガーフレーズは次の手順(定型アクション設定)後には使わなくなるので、実は何でもよいです。

(追記)IFTTTのプラン仕様が変わり、無料プランだと作れるアプレットが3つまでになりました。無料プランを使う場合は、「仕事終わり」を省略しても成り立ちます。

以下のように4つ作成できればOKです。
「アレクサ、仕事開始をトリガー」のように呼び掛けて、一度アプレットを動かしておいてください。一度でも動かすと、指定したフォルダパス、シート名でスプレッドシートが自動生成されます。
![myapplet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114715/244a4d0d-8d64-f591-d2c2-8863da94df2a.png)

#### スプレッドシート準備
書き込み先のスプレッドシートには、後述のGASと整合性がとれたヘッダ行やシートが必要です。

ここまでの設定で、IFTTTから書き込み可能なスプレッドシートが生成されているはずなので、そのスプレッドシートを開き、ファイル > インポート で[こちらのテンプレート](https://github.com/uemuram/alexa-attendance/blob/master/%E5%8B%A4%E6%80%A0%E7%AE%A1%E7%90%86_%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88.xlsx)をインポートします。
インポート時に既存のファイルを上書きするかどうかを聞かれるので、「スプレッドシートを置換する」を選択して上書きするようにします。

### 定型アクション設定
ここまでで、「アレクサ、仕事開始をトリガー」「アレクサ、休憩をトリガー」等でスプレットシートに書き込まれる状態になっているはずですが、なるべく短いフレーズで動かしたいので、「アレクサ、仕事開始」「アレクサ、休憩」等だけで起動するようにします。
定型アクションを設定すると、自分で決めた短いフレーズで色々なアクションを動かせるので、それを利用します。
![定型アクション3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114715/2f799537-7b6c-3368-ec45-56354e9e0395.jpeg)
上図のようにアレクサアプリの定型アクションで「IFTTT」アクションを追加し、作成済みのIFTTTアプレットを指定します。
こちらもアプレット数分(4種類)が必要です。

また、そのままだと定型アクション起動時には「わかりました」としか返してくれず味気ないので、アクションとして「Alexaのおしゃべり」-> 「カスタム」を追加して、Alexaに好みの一言を話してもらうのがいいと思います。
自分は上図のように、仕事開始時には、「仕事開始です。今日も頑張りましょう」と言ってもらっています。

### GAS設定
GASを設定していきます。
GASにはContainer Bound Script(スプレッドシートに埋め込まれたスクリプト)とStandalone Script(独立したスクリプト)がありますが、今回はContainer Bound Scriptを利用します。

先ほど準備したスプレッドシートを開き、「ツール」->「スクリプトエディタ」でスクリプトを作成します。
ソースは[こちら](https://github.com/uemuram/alexa-attendance/blob/master/%E3%82%B3%E3%83%BC%E3%83%89.gs)から取得して貼り付けてください。
![script_editor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114715/26611fcd-6bf0-b89d-e267-c716891c34c1.png)

続いてトリガーを設定します。
スクリプトエディタから、「編集」->「現在のプロジェクトのトリガー」でトリガー設定画面を開き、「トリガーを追加」で追加します。
![trriger.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114715/0f486128-7073-0f08-dc88-d95083baae8c.png)

トリガー設定は、「スプレッドシートから」「変更時」とします。「編集時」だとIFTTTからの書き込み時にスクリプトが動かないので注意です。
![trriger2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114715/2b898fa2-812c-9bae-af16-9c5c89479c0c.png)

トリガー保存時に承認を求められた場合は、[こちら](https://qiita.com/SyogoSuganoya/items/397a8bbb1636c3f43524)を参考にして実行許可を与えておきます。

#### GAS内の処理について
GAS内の処理について、ポイントになる部分を少し書いておきます。

- IFTTTから書き込む際は、先頭のシートの一番下に1行追加される形になるので、以下のように値を取得できます。

```javascript
const RECORD_DATE_COL = 1;
let recordSheet = spreadsheet.getSheetByName("作業記録");
// 一番下から上方向に向かって調べ、最初に見つかった値入りセルを取得
let lastCell = recordSheet.getRange(recordSheet.getMaxRows(), RECORD_DATE_COL).getNextDataCell(SpreadsheetApp.Direction.UP);
```
- IFTTTから渡す{{TriggeredAt}}は「April 28, 2020 at 02:41PM」のような独自の日付時刻形式なので、以下のようにDate型への変換が必要です。

```javascript
const MONTHS={
  "January":0, "February":1, "March":2, "April":3, "May":4, "June":5,
  "July":6, "August":7, "September":8, "October":9, "November":10, "December":11,
};

// April 28, 2020 at 02:41PM → 4/28 14:41
function iftttDateStr2Date(str){
  let match = str.match(/([a-zA-Z]+) (\d+), ([\d]{4}) at (\d+):(\d+)(AM|PM)/);
  if(match){
    let year = match[3];
    let month = MONTHS[match[1]];
    let day = match[2];
    let ampm = match[6];
    let hour = (ampm == 'PM' && match[4] <= 11) ? Number(match[4]) + 12 : match[4];
    let minutes = match[5];
    return new Date(year,month,day,hour,minutes);
  } else {
    return null;
  }
}
```

### 確認
最後に動作確認をしておきます。
「アレクサ、仕事開始」「アレクサ、休憩」「アレクサ、仕事再開」「アレクサ、仕事終わり」で時間の集計が動作すれば成功です。

# おわりに
Alexaに向かって仕事の終わりをきちんと宣言することでメリハリをつける効果もありそうです。
興味がわいた方は試していただければと思います。