Raspberry Pi を「サイネージ端末」として使い、社内の Web アプリケーションを常時フルスクリーン表示する仕組みを構築した。
ブラウザの自動操作には Playwright (Python) を使用し、Chromium の --kiosk モードでフルスクリーン表示を実現している。
--no-fullscreen オプションでウインドウモードに切り替え可能curl -LsSf https://astral.sh/uv/install.sh | sh
pyproject.toml:
[project]
name = "rpi-signage"
version = "0.1.0"
description = "Automated Web page launcher for Raspberry Pi using Playwright"
requires-python = "==3.13.*"
dependencies = [
"playwright>=1.57.0",
"python-dotenv>=1.2.1",
]
uv sync
uv run playwright install chromium
playwright install chromium で、Playwright がブラウザを自動操作するために必要な Chromium バイナリがダウンロードされる。Raspberry Pi OS にプリインストールされている Chromium とは別のもの。
ただし、OS にインストール済みの Chromium がある場合はそちらを優先して使う (後述の get_browser_channel())。
#!/usr/bin/env python3
import argparse
import json
import shutil
import time
from pathlib import Path
import dotenv
from playwright._impl._errors import TargetClosedError
from playwright.sync_api import BrowserContext, sync_playwright
COOKIES_FILE = Path(__file__).parent / "cookies.json"
def save_cookies(context: BrowserContext):
"""Cookie を JSON ファイルに保存する"""
try:
cookies = context.cookies()
COOKIES_FILE.write_text(json.dumps(cookies, ensure_ascii=False))
print(f"Saved {len(cookies)} cookies.")
except Exception as e:
print(f"Failed to save cookies: {e}")
def load_cookies(context: BrowserContext):
"""保存済みの Cookie を読み込んでブラウザコンテキストに適用する"""
if not COOKIES_FILE.exists():
return
try:
cookies = json.loads(COOKIES_FILE.read_text())
context.add_cookies(cookies)
print(f"Loaded {len(cookies)} cookies.")
except Exception as e:
print(f"Failed to load cookies: {e}")
def get_browser_channel() -> str | None:
"""OS にインストールされた Chromium/Chrome を検出する。
なければ None を返し、Playwright バンドル版を使用する。"""
if shutil.which("chromium") or shutil.which("chromium-browser"):
return "chromium"
if shutil.which("google-chrome") or shutil.which("chrome"):
return "chrome"
return None
def main(args):
with sync_playwright() as p:
channel = get_browser_channel()
chromium_args = [
"--window-size=1920,1040",
"--window-position=0,0",
]
if not args.no_fullscreen:
chromium_args.append("--kiosk")
context = p.chromium.launch_persistent_context(
user_data_dir="./browser_data",
headless=False,
channel=channel,
args=chromium_args,
no_viewport=True,
ignore_https_errors=True,
)
page = context.pages[0] if context.pages else context.new_page()
load_cookies(context)
page.goto("https://your-webapp.example.com/")
time.sleep(2)
# ログインページにリダイレクトされた場合の認証処理
if page.locator("#username").count() > 0:
print("Logging in...")
page.fill("#username", dotenv.get_key(".env", "USERNAME"))
page.fill("#password", dotenv.get_key(".env", "PASSWORD"))
page.click("button[type='submit']")
time.sleep(3)
save_cookies(context)
# Ctrl+C で終了するまで無限待機
print("Press Ctrl+C to exit.")
try:
while True:
time.sleep(2)
except KeyboardInterrupt:
save_cookies(context)
except TargetClosedError:
print("Browser window was closed.")
context.close()
if __name__ == "__main__":
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument(
"--no-fullscreen",
action="store_true",
help="フルスクリーンにしない。開発時に指定する。",
)
args = arg_parser.parse_args()
main(args)
launch_persistent_contextPlaywright の通常の launch() ではなく launch_persistent_context() を使っている。
user_data_dir にブラウザのプロファイルデータ (キャッシュ、ローカルストレージなど) が永続化される。
save_cookies() / load_cookies() で、Cookie を JSON ファイルに保存・復元する。
launch_persistent_context でもある程度セッションは維持されるが、明示的に Cookie を保存することで確実にログイン状態を復元できる。
get_browser_channel()Raspberry Pi OS には chromium-browser がプリインストールされている場合がある。
shutil.which() でシステムにインストールされた Chromium / Chrome を検出し、あればそれを使う。なければ Playwright バンドル版の Chromium を使用する。
OS ネイティブの Chromium の方が、Raspberry Pi 上でのパフォーマンスが良い傾向にある。
no_viewport=TruePlaywright はデフォルトでビューポートサイズを固定する。no_viewport=True を指定すると、ウインドウサイズに応じてビューポートが動的に変わるようになる。サイネージ用途では画面全体を使いたいのでこの設定が必要。
.env からの認証情報読み込みパスワードなどの認証情報は .env ファイルに記載し、python-dotenv で読み込む。
USERNAME=your-username
PASSWORD=your-password
Raspberry Pi 上で Chromium をフルスクリーン表示するために、いくつかの方法を試した。
xdotool key F11シェルスクリプトから遅延実行で F11 キーを送る方法。
sleep 10 && xdotool key F11 &
タイミングの問題で安定しなかった。ブラウザが完全に起動する前に送信されたり、フォーカスが合わなかったりする。
--start-maximizedChromium の起動オプション。ウインドウは最大化されるが、アドレスバーやタブバーは残る。サイネージ用途には不十分。
requestFullscreen()Playwright から JavaScript を実行してフルスクリーンにする方法。
page.evaluate("document.documentElement.requestFullscreen()")
ユーザーのジェスチャー (クリックなど) を伴わないプログラム的な呼び出しは、ブラウザのセキュリティポリシーによりブロックされる場合がある。
--kioskchromium_args.append("--kiosk")
Chromium の --kiosk フラグは、アドレスバー・タブバーを含むすべての UI 要素を非表示にし、完全なフルスクリーンでページを表示する。
キオスク端末 (公共の端末) 用に設計されたモードで、サイネージ用途にも最適。
開発時は --no-fullscreen オプションを付けることで --kiosk を無効にし、通常のウインドウモードで動作確認できる。
# 通常起動 (キオスクモード)
uv run ./open_signage.py
# 開発時 (ウインドウモード)
uv run ./open_signage.py --no-fullscreen
自動起動時に実行するシェルスクリプト:
#!/usr/bin/env bash
set -e
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting signage"
cd "$(dirname "$0")"
export DISPLAY=:0
/home/pi/.local/bin/uv run ./open_signage.py
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Signage finished"
export DISPLAY=:0 は、GUI のない autostart 環境から X Window System にアクセスするために必要。
.desktop ファイル~/.config/autostart/ に .desktop ファイルを配置することで、Raspberry Pi のデスクトップ起動時に自動的にスクリプトが実行される。
[Desktop Entry]
Type=Application
Name=Web Signage
Exec=/bin/bash -c "/home/pi/signage/autostart.sh >> /tmp/signage.log 2>&1"
Terminal=false
X-GNOME-Autostart-enabled=true
ログを /tmp/signage.log にリダイレクトしているので、問題が発生した場合はこのファイルを確認する。
scp でファイルを転送するシンプルなデプロイスクリプト:
#!/usr/bin/env zsh
set -e
cd "$(dirname "$0")"
FILES=(
autostart.sh
open_signage.py
.env
pyproject.toml
)
for FILE in "${FILES[@]}"; do
scp "$FILE" pi@rpi-signage.local:~/signage/
done
scp signage.desktop pi@rpi-signage.local:~/.config/autostart/signage.desktop
デプロイ後、ラズパイを再起動すれば新しいスクリプトが自動起動される。
ラズパイ側で初回のみ以下を実行しておく:
mkdir -p ~/signage
mkdir -p ~/.config/autostart
cd ~/signage
uv sync
uv run playwright install chromium
--kiosk フラグが Chromium のフルスクリーン化には最も確実.desktop ファイルで OS 起動時に自動起動uv でシンプルに依存関係を管理scp ベースのデプロイスクリプトで Mac からラズパイに簡単にデプロイ