作業に集中しすぎて予定を忘れた人が Windows の Toast で時報を通知してみた

# このメモは何か？

* 前半には **ワークに専念しすぎてミーティング参加が遅れがちになった人の考えの過程** が書かれています。
* 後半には **タスクスケジューラ + PowerShellで定期的にToast通知するコード** が書かれてます。
* コードを実行すると、結果、１時間に１回、図のような通知がされるようになります：

![hourly-notification.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296582/ec91c2fd-dafa-d764-5882-4154cfec8b60.png)

経緯はどうでもよくて、通知だけしたいという人はコードの部分だけ見ればよいです。所要時間3分くらいでできます。

# はじめに

私の場合：

* チーム外メンバとのワークは毎時ゼロ分からの予定が多く、最近、そのような頻度が多くなってきました。
* そのため、チーム内外の自分のワークがN時間で区切られることが多くなってきました。
   * 今では、可能な限り、ワークを1時間ごとに区切るように心がけています。

...という状況で、ワークに集中するほど、時刻気づきにくくなることがあります。

~~ミーティング開催に気付かず欠席することはいなくても、実は何とかなる場合も結構多かったりして意図せず欠席することは有益なこともあったりしますが、~~欠席すべきでなかったり、欠席したくないミーティングに欠席するのは良くない。

集中を保てるよう時刻を意識せず、時刻に気づきやすい状況にしたい。

ワークを切り替えることが苦手なので、頭の中にある情報をメモに吐き出して、ミーティング準備も含め、最低でも5分は欲しい。

# どう対処すればいいか？

通知手段として何が良いか、その手段をどう実装すればいいかを考えればよい。

## 通知手段を検討する

人に刺激を与えるなら五感。
五感の何に変化を与えるか。
視覚、聴覚、触覚...。

結論としては、 Windows で Toaster で時報を表示するのが良いと至った。

主に、通信環境が無い状態でも気づけるようにする、かつ、追加デバイスがなく煩雑でないことが理由となった。

### 通知手段: 視覚

* 周囲の人が知覚できてしまう
   * RaspberryPi等でLEDの発光で知らせる。
      * Pros: [Crystal Signal Pi](https://www.infiniteloop.co.jp/blog/2016/11/release-crystal-signal-pi/) などであれば穏やかな通知ができるが周りの人の目に光を入れることになるが憚られる。
      * Cons. Raspberry Piの稼働のためのインフラを用意するのは意外とめんどそう。電源、時刻同期、壊れた時の修復過程、etc.
* 自分のみ気づく
   * 各種予定管理(予定表、メーラー)のリマインド機能を使う。
      * Cons. 環境に依存してしまう。常に予定管理ができる環境にあるかというと、そうでもない気がする。最悪、無線LANすら接続できない状況もあると思う。
   * 携帯電話で通知を受ける。
      * Webサービスを仕立てて、Webブラウザの通知で振動させたりできるのではないか。
      * Pros. 通信環境が悪いことは最近はほとんどなくなった。
      * Cons. 通知の経路としてSMSはコストがかかるので回避。体の姿勢によって、気づくいにくい。たとえば前かがみだと胸ポケットに入れた携帯の振動に気づけなかったりする。充電中などは通知に気づかないかもしれない。
   * ARゴーグル。
      * 資金が足りない。手軽に壊せなさ過ぎてこわいｗ
   * 【採用】Windows 端末のローカルで通知する。
      * Pros. 通信環境に依存しない。追加デバイスが不要である。コストがかからない。電源落ちてるときは大抵通知受けなくてもよい働き方をしているので(思案中、休憩中、業務外など)、まあ、問題ない。
      * Windows だと [Toast](https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/toast-desktop-apps)で通知するのが良さそう。

### 通知手段: 聴覚

* 周囲の人が知覚できてしまう
   * 音を鳴らす。
      * Cons. 光同様、周りの人の耳にも届くのがマイナス。
* 自分のみ気づく
   * 耳付近で音を鳴らす。骨伝導なんかもここ。
      * Pros. bluetooth で周りの人の声を聴ける、かつ、通知音が聞こえるようにできるのでは。
      * Cons. 充電、ペアリングの意図しない解除で気づかなくなりそう。耳が痛くなりそう。

### 通知手段: 触覚

* スマートウォッチを振動させる。
   * Pros. 気づきやすい。
   * Cons. 充電が手間なのと肌がかぶれ易いのがつらい。
   * 充電・肌で問題ないなら他の候補を抑えて最強では...。

### 通知手段: 嗅覚

* 周囲の人が知覚できてしまう
   * 時刻指定でコーヒーなどの良いと感じるにおいで気づかせる。
      * 時刻指定でコーヒー作れる機械はあるだろう。
      * 周囲が否定なく良いと思う香りであれば、周囲が知覚できてもよいはず。コーヒーが良いかはわからないが。
      * Pros. コーヒーだとしてコーヒーを飲める。
      * Cons. コーヒーだとして1時間に1回は多すぎるし豆コストもばかにならない。洗う手間も。不在の時はコーヒーだけができてしまうか、不要なコーヒーを作らないための仕掛けがめんどそう。

## Toast設計

* Toast通知については https://github.com/onokit/toaster を利用するのが簡単そう。
   * 一部機能が足りないことが分かっているので、プルリクエストする。マージが終わるまではフォークしたリポジトリを使う。
      * README.md などの更新からプルリクして様子を見るのが良い？その後機能追加もプルリクしていく。
      * 通知の有効期限切れを設定するオプションを設定したいが、これを設定する機能を加えたい。
* あまりに集中すると、ウィンドウ外に目が届かなくなるので、Toast通知は自動で消えないようにする。
   * とはいえ、1時間に1回の通知を消さずに放置すると通知が溜まる。溜まるのは困るので、Toastの有効期限を設定する。
* 稀に様々な理由でN時30分から予定を入れる人がいるので、30分のスヌーズがあるとよい。

## タスクスケジューラー設計

* タスクスケジューラのトリガは、1日に1回、週ごとに実行などの手段があるが、1時間に1回実行することはできない。
   * タスク数をいくつにするか？(1 or 24) → 1個
      * 実行コマンドや引数などをタスクごとに分けないのであれば、1タスク24トリガーの方が管理が容易となる。
      * 1時間ごとに異なるToast用のXMLを用意するといったことはイベントスケジューラーだけではできない。この場合はXMLを選択しToast通知を行う処理を別途用意する必要がありそう。
   * タスクのトリガー数をいくつにするか？(1 or 24) → 24個
      * 1トリガーに実行間隔を指定することができ、GUIでは1時間に1回を1日間といった指定ができる。
         * PowerShell で1時間に1回を1日間、を指定する手段が Get-Help からは見当たらなかった。
      * タスクを消すと、タスクに紐づくトリガーも消される。1タスクに24トリガーでもさほど問題なさそう。

# コード化の指針

* コマンド実行はタスクスケジューラーで行う。
* 平日日中帯に通知があればよい。
    * それ以外で通知があっても問題はない。
* 通知が消えないようToastは「リマインダ」にする。
* 毎時53分に通知する。
    * 通知を受けた人は7分でワークのコンテキストスイッチする。
       * 脳、ファイル、リモートなら部屋移動・音量・ビデオ設定等、次ワークの準備

# コード

PowerShellを起動して、次のコードをコピー＆ペーストして実行すればよい。

実行すると、タスクスケジューラに毎時55分にToast表示するタスクを登録する。

```powershell
# Toast通知のツール "toaster" をインストールする
# https://github.com/onokit/toaster を使わないのは、通知時間の有効期限切れの機能をPRしてマージいただくことができていないため。
# パス長が長くならないよう、ここではc:\を指定する。
cd c:\
git clone https://github.com/kumarstack55/toaster.git

# Toast通知用のXMLをインストールする
cd c:\
git clone https://github.com/kumarstack55/windows-toast-xml.git

# 作成するタスク名
$TaskName = "my-task-toast-hourly"

# 毎時何分で通知するか
$Minute = 53

# タスクスケジューラーでタスク名の重複は許されないため、
# すでに同名のタスクがあれば消す
Get-ScheduledTask -TaskName $TaskName | Unregister-ScheduledTask

# パスを設定する
$LauncherPath = "C:\toaster\powershellLauncher.vbs"
$XmlPath = "C:\windows-toast-xml\xml\hourly-notification.xml"
$ToasterPath = "C:\toaster\toaster.ps1"
$WorkingDirectory = "C:\toaster"

# 毎時分のタスクスケジューラに登録する
$TriggerArray = @()
0..23 |
ForEach-Object {
    $Hour = $_
    $At = Get-Date -Hour $Hour -Minute $Minute -Second 0 -Millisecond 0
    $TriggerArray += New-ScheduledTaskTrigger -Daily -At $At
}
$Action = New-ScheduledTaskAction `
    -Execute $LauncherPath `
    -Argument "$ToasterPath -XmlPath $XmlPath -ExpirationDurationMinutes 55" `
    -WorkingDirectory $WorkingDirectory
Register-ScheduledTask `
    -TaskName $TaskName `
    -Action $Action `
    -Trigger $TriggerArray

# 次回実行時刻を確認する
Get-ScheduledTaskInfo -TaskName $TaskName
```

指定したXMLは次の通り。

```xml
<toast scenario="reminder">
  <visual>
    <binding template="ToastGeneric">
      <text hint-maxLines="1">Hourly notification</text>
      <text>Be prepared for the next work.</text>
    </binding>
  </visual>
  <actions>
    <input id="snoozeTime" type="selection" defaultInput="5">
      <selection id="5" content="5 minutes"/>
      <selection id="30" content="30 minutes"/>
    </input>
    <action activationType="system" arguments="snooze" hint-inputId="snoozeTime" content="" />
    <action activationType="system" arguments="dismiss" content=""/>
  </actions>
</toast>
```

# コードを実装してわかってきた課題

* Toastのウィンドウをクリックすると「解除」相当の処理になる →問題ない
   * リマインダーの場合、ウィンドウ内の解除をクリックしない限り再通知されそうに思うかもしれないが、ウィンドウのボタン以外の領域をクリックするだけで、解除相当の操作となる。そのため、誤ってウィンドウをクリックするとなんの通知かわからないという状況になる。Toastの仕様なのではないか。右下でウィンドウ操作をしないというのが対処策に見える。悲しい。まあ問題ない。
* 1時間以上放置したとき、通知されない時間帯がある →問題ない。
   * リマインダの場合、解除しない限り、画面右下に通知が残る。これは良い。ただし、1時間以上放置した場合、例えば 9:55am の通知を 10:56 に解除した場合、10:55の通知は行われないと思う(未確認)。まあ、右下にOS標準の時計あるし、問題ないだろう。
* 日本語でメッセージ表示したらどうなるのだろうと試したら文字化けした。→未解決。保存エンコーディング、BOM有無が気になる。
* Heroイメージを表示させようとしたら手元の環境では表示できなかった。→まあ、問題ない。
* 1時間のワークに5minのオーバーヘッドが発生することは許されるのか。
   * 偉い人はあんまり考えてなさそう。気づいてないので暗黙に許される。
   * 許されるが、望ましくない。
   * 要集中の自己ワークと、ミーティングが交互に入ると、有効に使える時間はSave/Loadも考えると-10%程度になるのでは。
   * 集中したい作業をまとめて確保するよう周囲を調整するのが良い。
   * それが確保できなくなったら、エンジンである自分の出力不足が問題か、あるいは、ヒューマンリソースを管理するマネージャが悪いか、あるいは、両方に問題がある。
* PCの電源をつけっぱなしにして数時間放置すると、Toast通知が溜まる。→Toastに有効期限を設定すればよい。
* タスクスケジューラの実行時刻の精度がよくない？→問題ない。
   * NextRunTimeを確認すると、時・分は適切だが、指定秒と異なる時刻が表示されるケースがあった。通知時刻を前倒しすればよい。

# 参考資料

## 参考資料: Toast

* Toast notifications from desktop apps
   * https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/toast-desktop-apps
* Windowsトースト通知でリマインダ
   * https://qiita.com/onoki/items/d85ca3c052ff436e7888
* Toast content XML schema
   * https://docs.microsoft.com/ja-jp/windows/uwp/design/shell/tiles-and-notifications/toast-xml-schema
* Toast content
   * https://docs.microsoft.com/ja-jp/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts
* Toast Notifier for Windows.
   * https://github.com/onokit/toaster
* Set an expiration time
   * https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/send-local-toast#set-an-expiration-time

## 参考資料: タスクスケジューラ

* PowerShellでタスクスケジュール登録
   * http://www.vwnet.jp/windows/HS12R2/Schdule/Task.htm