BOOX Leaf2 を E Ink の卓上時計にした
電子ペーパー端末の BOOX Leaf2 に、自作の時計ページを表示し続けています。時刻・天気・室温・湿度・CO2・電池を白黒 2 値で出し、Home Assistant の値は nginx の中継で読むのでトークンを端末に置きません。E Ink に合わせた画面の決まりと仕組みをまとめます。
目次
BOOX Leaf2(Android 11 の電子ペーパー端末)を、机の上に置いて時計にしています。ブラウザで自作の時計ページを開いたままにして、時刻と日付のほか、今日と明日の天気、室温・湿度・CO2、端末自身の電池の残量を出しています。
値は、手元のサーバーで動かしている Home Assistant(以下 HA)から読みます。ページを配信している nginx が HA への問い合わせを中継してトークンを付けるので、端末にはトークンを置いていません。
この記事では、E Ink の画面に合わせて決めたこと(白黒 2 値、強調は反転、値が変わったときだけ書き換える)と、HA とのつなぎ方、時刻だけの最初の版から今の形になるまでを書きます。

作ったきっかけ
今の事務所に移って数か月、大きな時計がなくて地味に不便でした。時計を買おうかとぼんやり考えてはいたものの、絶対に必要というほどでもありません。ちゃんとしたものを買おうとするとそれなりの値段がしますし、「これだ」と思える時計も見つからず、気がつくと後回しになっていました。
そんなとき、E Ink のディスプレイで置き時計を作っている記事を見かけて、これなら自分でも作ってみようと思いました。最初は E Ink のディスプレイを買って自作するつもりでしたが、そこで思い出したのが BOOX Leaf2 です。数年前に買ったものの、iPad mini を買ってからは押し入れにしまったきりで、まったく使っていませんでした。
これを使えば、ディスプレイを別に買わなくても時計が作れる。そう考えて、Leaf2 を時計にすることにしました。
立てるためのスタンド
Leaf2 を机に立てておくスタンドも、3D プリンターで自作しました。Leaf2 の寸法を AI に調べてもらい、それをもとにやりたいことを伝えて、3D モデルも AI に作ってもらいました。印刷してみると、一発で使えるスタンドができました。

画面に出しているもの
画面は横向きで使っています。上から順に、次のものを並べています。
- 日付と曜日、右上に電池の残量
- 時刻(文字の大きさは画面の幅の 30%)
- 今日と明日の天気(アイコンと、最高・最低気温)
- 室温・湿度・CO2(数値と横棒)
時刻と日付は、端末の時計から描きます。HA から読むのは天気と室温・湿度・CO2 です。室温・湿度・CO2 は SwitchBot の Meter Pro CO2 の値です。
電池の残量は、ブラウザの Battery Status API(navigator.getBattery())で端末から取ります。この API は HTTPS のページでしか使えないブラウザが多いので、取れない環境では電池の表示を出さないだけにして、時計には影響させないようにしました。Leaf2 のブラウザでは http のページでも取れています。
横棒の範囲は、温度が 15〜35°C、湿度が 20〜80%、CO2 が 400〜1600ppm です。ふだんの値が真ん中あたりに来るように取りました。CO2 の棒の下には、1000ppm の位置に短い目印を付けています。
E Ink に合わせた 3 つの決まり
白と黒だけで描く
背景は純白、文字は純黒にして、中間調(グレー)とアニメーションは使いません。E Ink ではグレーが見分けにくいからです。天気のアイコンも線画だけにして、塗りは白か黒のどちらかにしています。
強調は白黒の反転で
グレーに頼れないので、目立たせたいところは白黒を反転させます。今は次の 2 か所です。
- CO2 が 1000ppm 以上のときの、CO2 の数値
- 電池が 20% 以下で、充電していないときの電池の表示

電池の表示を反転させると、今のページでは電池のアイコンが黒地に溶けて見えなくなり、白い数字だけが残ります(アイコンの線を黒で描いているため)。
反転しても文字の位置が動かないように、数値のまわりの余白は反転していないときも確保しています。前の版のコードのコメントには、E Ink は位置がずれると残像が出るため、と書いていました。
値が変わったときだけ書き換える
ページの中身(DOM)を書き換えると、E Ink の画面が描き直されます。そこで、表示する文字が前と同じなら何もしないようにしました。
時刻は 1 秒ごとに確かめますが、描き直すのは分が変わったときだけです。分が変わったときに、天気・室温・電池もまとめて取り直します。
トークンを端末に置かない:nginx の中継
HA の API を読むには、アクセストークンが要ります。ページにトークンを書き込むと、ページを開ける人なら誰でも読めてしまいます。
そこで、時計ページを配信している nginx に HA への中継を持たせました。
- ページは
/ha/temperatureのような、同じサーバーのパスを読むだけ - nginx がそれを HA の API(
/api/states/<センサー>)に中継し、そのときにトークンを付ける - トークンは root だけが読める nginx の設定ファイルに置き、端末には渡さない
- 中継するのは決めたセンサーだけで、HA の API 全体は外に出さない
毎分の電池の報告も /ha/battery-report で同じ nginx を通し、HA の webhook に渡しています。webhook はトークンが要らないので、ここはトークンを付けずにそのまま中継します。webhook の側は、POST だけ、LAN からだけ受けるように設定しています。
ページと問い合わせ先が同じオリジン(同じサーバーとポート)になるので、ブラウザのクロスオリジンの制約も受けません。電池の報告を HA のポート(8123)に直接送っていたときは、最初の 1 回しか届きませんでした。
こうして HA に届けた電池の残量は、Leaf2 の充電の自動化に使っています。その話は「SwitchBot プラグミニで E Ink 時計の充電を自動化した話」に書いています。
天気は 1 つのセンサーにまとめる
天気は、HA の met の予報を使っています。HA の天気のエンティティは予報を属性に持っていないので、予報を取るには weather.get_forecasts を呼ぶ必要があります。
そこで、HA のテンプレートセンサーで 30 分ごと(と HA の起動時)に予報を取り、今日と明日の天気・最高気温・最低気温を 1 つのセンサーの属性にまとめました。こうすると、nginx の中継も天気の分は 1 本で済みます。
最初の版から今の形まで

最初から今の形だったわけではありません。
- 最初の版: nginx を入れて、時刻と日付だけの時計を作りました(画像の 1)
- その日のうちに: HA を構築し、Meter Pro CO2 を登録して、nginx の中継の設定を作りました
- 2 つ目の形: 室温・湿度・CO2 を 1 行に並べ、右上に電池の残量を出しました(画像の 2)。この頃には、HA の
/local/にページを置く形も試しています(/local/は認証なしで配信されるのでトークンを埋め込めず、値は HA がファイルに書き出す形) - 今の形: 天気の欄を足し、室温・湿度・CO2 を横棒つきで下にまとめました(画像の 3)。最初の版から 1 週間あまりで、ここまで来ました
2 つ目の形では、端末のフォントによってはセンサーの行が画面の幅に収まらないことがありました。折り返したら少しずつ文字を小さくする処理を入れていましたが、今の版では 3 つを横に並べた枠に分けています。
この記事の画面の画像(写真以外)は、実際のページ(と以前の版)を Playwright で開き、HA への問い合わせの応答をダミーの値に差し替えて撮ったものです。表示している日時・天気・数値は実際のものではありません。
使ってみて便利だったところ
最初は、時刻が分かればいいくらいのつもりで作り始めました。ただ、この仕組みはブラウザで開けるものなら何でも表示できます。CO2 などを測る仕組みはもともとあって、それを画面に出す部分も、今は AI に頼めば簡単に作れます。
そのおかげで、出したいものを気軽に足せました。どうせなら CO2 も出したい。CO2 を出すなら、ついでに温度と湿度も。それでも画面の余白が余っているから、天気も出そう。そんなラフな思いつきを、すぐに形にできたのがすごく便利でした。
最近は手を入れていませんが、今の画面は、たぶん 1 日に何回も、無意識のうちに見ています。作って終わりではなく、生活の一部になっている。本当に実用的なものが作れたのがよかったと思っています。


