生活 自作PC

リビングに「今日の情報」を全部出したい。生成AIと作った情報ダッシュボード制作記(中編)

投稿日:

前編では、天気・ゴミ出し・月齢・潮汐・家族の予定(TimeTree)をWebページとして表示するところまで作った話を書きました。中編では、それを「リビングで常時表示する」ための試行錯誤と、電車・バスのリアルタイム情報を組み込んだ際にハマったエラーの話を書きます。

常時表示、何で実現する問題

Webページとして動くところまではできたものの、「これを24時間リビングに表示し続ける」となると、話が変わってきます。検討した選択肢は主に3つでした。

案1:ノートPCを置きっぱなしにする

一番手軽ですが、画面焼き付き、消費電力、ファンの音、そして単純に「置き場所」の問題がありました。ノートPCをリビングに常設するのは、見た目的にも生活動線的にもしっくりきませんでした。

案2:Raspberry Piでキオスク端末を作る

自作の情報ディスプレイというと定番の選択肢です。実際に構成を調べて、Raspberry Piにブラウザをキオスクモードで起動させる方法もかなり検討しました。ただ、今回は「まず動くものを早く作って生活の中で試したい」という気持ちが強く、本体の購入・OSセットアップ・自動起動設定など、準備に時間がかかりそうな点がネックになりました。結局、今回は見送っています。

案3:Fire TV Stickでブラウザアプリとして表示する

最終的に選んだのがこの方法です。Fire TV Stickは元々リビングのテレビや、今回用意した15.6インチのモバイルモニターに挿すだけで使えるため、追加のセットアップの手間が少なく済みます。

リビングの棚に設置したダッシュボード用モニターとFire TV Stick(匿名化済み)
リビングの棚に常設したダッシュボード。ガラス扉の収納棚に立てかける形で設置しています

我が家では、リビングのテレビとは別に、常時ダッシュボード専用としてFire TV Stick 4K MaxJAPANNEXTの15.6インチモバイルモニターに挿しっぱなしにする形にしました。テレビだと家族が別の番組を見たいときに画面を奪ってしまいますが、専用の小型モニターを別に用意することで、ダッシュボードとテレビ視聴を共存させています。省スペースで置ける15.6インチというサイズ感も、リビングの一角に置くにはちょうどよいバランスでした。

Fire TV Stick上でWebアプリを表示する方法としては、Fire TV向けのWebViewアプリ(Android向けのWebViewをベースにしたアプリ)を作り、その中で自作のダッシュボードのURLを開く、という構成にしています。ここは生成AIとやり取りしながら、AndroidのWebView実装の基本的なひな形を組み立ててもらいました。

「常時表示」特有の細かい調整

普通のWebページと違い、四六時中同じ画面をつけっぱなしにするとなると、いくつか気を配る点が出てきます。

  • 画面が暗転しない設定:Fire TVのスリープ設定や、WebView側でのスクリーンオフ防止処理を入れる必要がありました。
  • 電源復帰時の自動起動:停電やブレーカーが落ちた後、手動で操作しなくても勝手にダッシュボードアプリが立ち上がるように、起動時に自動でアプリを起動する設定を組み込みました。
  • 時々固まる問題への対処:長時間表示していると、稀に画面がフリーズすることがあり、一定時間ごとに自動でページをリロードする仕組みを追加しています。

このあたりは「一度作って終わり」ではなく、実際に数日〜数週間リビングに置いて使ってみて初めて気づいた問題ばかりでした。動かしてみないとわからない類のバグは、生成AIに聞いても事前には出てこないので、結局は自分の家で実際に運用しながら直していく形になりました。

電車・バスのリアルタイム情報を足したくなった

ダッシュボードが常時表示できるようになると、欲が出てきます。次に欲しくなったのが、「今、家を出たら何分でバス(または電車)に乗れるか」というリアルタイムの交通情報でした。

時刻表を表示するだけなら簡単ですが、実際に知りたいのは「今日、電車やバスが遅れていないか」というリアルタイムの運行情報です。ここで見つけたのが、公共交通オープンデータの活用コンテスト「ODPT Challenge 2026」で提供されている公共交通オープンデータAPIでした。鉄道各社・バス事業者の運行情報や時刻表データがAPI経由で取得できます。

403エラーでまるまる1日溶かした話

APIキーを取得して意気揚々と実装を始めたのですが、最寄りの路線の運行情報を取得しようとすると、なぜか403エラー(アクセス拒否)が返ってきて、まったく先に進めない時間が続きました。

生成AIに実装を手伝ってもらいながら進めていたのですが、AIが提案してくるコードも最初はこのエラーの原因を特定できず、「APIキーが間違っているのでは」「エンドポイントのURLが違うのでは」と、的外れな仮説を何度も試しては潰す、という時間がかなりかかりました。

最終的に自分でAPIドキュメントを読み込んで原因を特定できたのは、リクエストのパラメータ指定に問題があったというオチでした。運行情報を取得する際に指定する事業者コード・路線コードの組み合わせが、一部の路線では通常のパターンと異なる特殊な指定が必要だったのです(複数の会社が乗り入れている路線など、一般的な私鉄・地下鉄の指定方法がそのまま通用しないケースがありました)。

このあたりは「AIに聞けば一発でわかる」ものではなく、公式ドキュメントの隅々まで自分で読んで、実際のレスポンスをひとつずつ確認しながら潰していく地道な作業でした。生成AIは実装のたたき台を作るスピードを大きく上げてくれますが、「なぜこのAPIがこの仕様になっているか」という一次情報の理解は、結局自分でドキュメントを読まないと得られないと痛感した場面です。

Promise.all の罠:一部のAPIが落ちると全部表示されない

複数の交通機関(電車・バス)の情報を同時に取得する処理は、最初 Promise.all() で書いていました。

// 最初の実装(問題あり)
const [trainData, busData] = await Promise.all([
  fetchTrainStatus(),
  fetchBusStatus(),
]);

Promise.all() は、渡した処理のうちどれか1つでも失敗すると、全体が失敗扱いになります。つまり、バスの情報取得だけがエラーになった瞬間、正常に取得できているはずの電車の情報まで巻き込んで、ダッシュボード全体の交通情報パネルが真っ白になる、という状態が発生していました。

これに気づいてから Promise.allSettled() に書き換えています。

// 改修後の実装
const results = await Promise.allSettled([
  fetchTrainStatus(),
  fetchBusStatus(),
]);
// 成功したものだけを画面に反映し、失敗したものはそのパネルだけ「情報取得できず」と表示

Promise.allSettled() なら、それぞれの処理が成功したか失敗したかを個別に受け取れるので、「一部のAPIが不調でも、取得できた情報だけは表示する」という、実際の生活で使う上で望ましい挙動にできました。地味な修正ですが、実運用に乗せてから気づいた改善点のひとつです。

APIキーの扱いについて

ODPTのAPIキーは、Fire TV向けのアプリをビルドする際にアプリ内に埋め込む形にしています。

# .envファイルのイメージ(実際の値ではありません)
ODPT_TOKEN=YOUR_TOKEN

正直に書いておくと、この方法はAPIキーの管理としては完全に安全とは言えません。アプリのビルド成果物を解析すれば、埋め込んだキーが読み取られてしまう可能性はゼロではないからです。本来、公開配布するアプリであれば、APIキーはサーバー側で管理し、アプリからはそのサーバー経由でデータを取得する構成にするべきです。

今回は、あくまで自分の家だけで動かす非公開のアプリであり、外部に配布する予定もないことから、このリスクは許容範囲と判断してビルド時埋め込みの方式を採用しています。もし同じようなダッシュボードを作って誰かに配布したり、公開したりする場合は、APIキーをサーバーサイドで管理する構成に変更することを強くおすすめします。

次回予告

中編では、常時表示化のための試行錯誤と、ODPT Challenge 2026 APIを使った交通情報連携、そこで踏んだエラーとその対処について書きました。

後編では、最終的にできあがったダッシュボードの全体像と、実際にリビングに設置してからの様子、そして生成AIとのやり取りを振り返って感じたことをまとめます。

(後編に続く)

google

google

-生活, 自作PC
-, , , , , , ,

Copyright© テストエンジニアしょうたろうの家庭菜園 , 2026 All Rights Reserved.