Electronで作った京浜急行運行情報監視ウィジェットを支えるクソコードたち

こんにちは、ポキオ（@pokiiio）です。[IoTLT Advent Calendar 2018](https://qiita.com/advent-calendar/2018/iotlt)の12日目を担当させていただきます。


![ポキオ 京急 運行情報 ウィジェット](https://lh3.googleusercontent.com/LPNEDI55eZcoN2ql9pXFM8jD-Mzi9S7IrCLhoC-_00xrElfpM7WWbbN9KrIg4v5ylYu45PP_fDERQ85X4Bqla5POngq8mkHMA68Rm1WFqJGq7Azn5SIN6tNwIp4NTvYBrGyAobmk4yM=s600 "ポキオ 京急 運行情報 ウィジェット")


# tl;dr


京浜急行の運行情報を監視するアプリをElectronで書きました。Electronなので、macOSでもWindowsでもきっと動くはずです。Node環境（当方はv8）があればすぐに使えますので、ぜひお使いください。


https://github.com/pokiiio/KeikyuWidget


上記リポジトリからソースをClone or Downloadして、


```bash
npm install
```


で必要なファイルをダウンロードし、


```bash
npm start
```

で起動します。また、パッケージ化したい場合は、


```bash
npm run build-macOS
```

で可能です。Windowsユーザーの方は、`build-macOS`の部分を`build-windows`に変えて実行してみてください。




# この期に及んでなぜElectron？


いままでIoTLTを中心に登壇させていただきましたが、それを同時に京急にまつわるガジェットをたくさん作成してまいりました。ご縁もありMakerFaireTokyo2018でも展示させていただきました。




![pokiiio Maker Faire Tokyo 2018](https://lh3.googleusercontent.com/4ZsPNlrSnuvorQt9Zsyd8GjnjUfGZzQly1NUvyZa1OnSLDeYE_UNkG0t2vINTA2_BaT0Tvf5omy58EcGcmvbivn02NGUg73qB-DOpV3CwyL33dMfuPYzQt4gLq0nyCnBJ7dMVqZFjlo=s600 "pokiiio Maker Faire Tokyo 2018")



ただ、物理的なガジェットの最大の欠点として、**持ち歩くのが面倒**という点が挙げられます。



そこで、今回は物理的なガジェットではなく、京急に関するデスクトップアプリを作りました。その実現方法として、いちばん最短ルートっぽかったのがElectronだったので、その紹介もさせてください。



> 勤務先のデスクには、過去作成したガジェットをいくつかおいてありますが、たとえばミーティングのために別の部屋に移動した際に、ガジェットを持っていくのは面倒です。でも、京急をつねに肌で感じていたい。そこでたどり着いたのがパソコン上で動作するデスクトップアプリでした。



# Electronとは

<img width="600" alt="Screen Shot 2018-12-05 at 0.50.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/212109/8d852aee-af56-edbb-8acb-9737aac18bb4.png">



https://electronjs.org/



ちょっと雑ですが、HTML+CSS+JSでクロスプラットフォームなデスクトップアプリが書ける仕組み、というふうに理解しています。豊富なnpmモジュールを使えるので便利な一方、BootstrapなどオサレなUIコンポネントも使えるので、ちゃちゃっとデスクトップアプリがかけてしまうのです。


# つくったもの

これ。


![ポキオ 京急 運行情報 ウィジェット](https://lh3.googleusercontent.com/5r7Fkb-CWmOF7MtnT0QisxECn3_415XFNldXLOmNyj20t2hZvftBexs1XVKaz2qVE0-PZjOx0syga01acdgVcqMiZ_DAs_fEQZC5K5eGid1HAQCXUU5ZWlbQ1mXCiDV1drkjjoprQcU=s600 "ポキオ 京急 運行情報 ウィジェット")

（表示内容はダミーです）


京急の運行情報を定期的に取得し、遅延などが発生しているときはデスクトップ上部に情報を表示するというものです。普段はアプリを起動しっぱなしにしておくのですが、何も表示されません。



# 技術的なポイント

以下の3つです。

- 京急の運行情報取得方法
- 運行情報だけを表示させる
- マルチスクリーン対応

## 京急の運行情報取得方法

これに関しては、今までのガジェット作成のノウハウがふんだんに（？）使われています。基本的には京急の情報ページ（ http://unkou.keikyu.co.jp/ ）から取得してくるのですが、


```html
<div class=unko-panel>
京急線は平常通り運転しています。
</div>
```

運行情報は`unko-panel`の`div`で囲まれています。したがって、JSでこれをパースするには、よしなに`request-promise`とかでbody全体を取得した後に、


```javascript
let info = body;
info = info.split('<div class=unko-panel>')[1];
info = info.split('</div>')[0];
info = info.trim();
return info;
```

こんな感じでゴリゴリパースすると、簡単に運行情報が取得できます。


## 運行情報だけを表示させる


通常、Electronアプリを作成して実行すると、Windowとして表示されてしまうので（あたりまえですが）、Windowを透明にしたり、メニューバーなども非表示にしてあげることで、あたかも運行情報だけが表示されてるかのように表現することができます。


具体的には、`BrowserWindow`をnewするときに、オプションで以下のパラメータを与えてあげます。


```javascript
width: size.width,
height: size.height,
// where size is display size.
frame: false,
transparent: true,
resizable: false,
alwaysOnTop: true,
```

- `width`・`height`には表示しようとしているディスプレイのサイズを与えて、全画面表示をする
- `frame`を`false`にしてウィンドウのタイトルバーを非表示する
- `transparent`を`true`にして、Windowを透明にする
- `resizable`を`false`にして、常に全画面表示とする
- `alwaysOnTop`を`true`にして、常に最前面に表示されるようする


ただし、最後の`alwaysOnTop`を`true`にしてしまうと、クリックイベントをすべてこのElectronアプリが奪ってしまうことになるので、下記のようにクリックイベントを下のレイヤーにスルーパスして上げる必要があります。


```javascript
window.setIgnoreMouseEvents(true);
```


## マルチスクリーン対応


仕事では、外部ディスプレイに接続してマルチスクリーンで仕事をすることも多いと思います。アプリでも、スクリーン数の変化を検知して、すべての画面に運行情報を表示できるようにしています。


```javascript
electron.screen.on('display-added', function () {
  ...
});

electron.screen.on('display-removed', function () {
  ...
});
```


こんな感じで`electron.screen`がEmitするイベントを受けるだけ。これをうけて、`electron.screen.getAllDisplays()`とかすると、接続中の画面がすべて取得できるので、それぞれの画面に対して`BrowserWindow`をnewして表示さればOK。


ただし「このDisplayに、このBrowserWindowを表示する！」みたいな便利なオプションはないので、画面の絶対座標を指定して、`BrowserWindow`をnewして上げる必要があります。


```javascript
let window = new BrowserWindow({
  x: display.bounds.x,
  y: display.bounds.y,
  ...
});
```



# サマリ


今回は、Electronでアプリを作ってみました。簡単にデスクトップアプリが作成できるので、みなさんもオレオレアプリを作ってはいかがでしょうか？