Raspberry Pi のサイネージ化 … Playwright で Chromium を最大化して自動操作する

2026-02-18 12:00 (8日前) ytyng
View in English

概要

Raspberry Pi を「サイネージ端末」として使い、社内の Web アプリケーションを常時フルスクリーン表示する仕組みを構築した。

ブラウザの自動操作には Playwright (Python) を使用し、Chromium の --kiosk モードでフルスクリーン表示を実現している。

この仕組みでできること

環境

セットアップ

uv のインストール

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",
]

Playwright と Chromium のインストール

uv sync
uv run playwright install chromium

playwright install chromium で、Playwright がブラウザを自動操作するために必要な Chromium バイナリがダウンロードされる。Raspberry Pi OS にプリインストールされている Chromium とは別のもの。

ただし、OS にインストール済みの Chromium がある場合はそちらを優先して使う (後述の get_browser_channel())。

Python コード

メインスクリプト

#!/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_context

Playwright の通常の launch() ではなく launch_persistent_context() を使っている。 user_data_dir にブラウザのプロファイルデータ (キャッシュ、ローカルストレージなど) が永続化される。

Cookie の永続化

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=True

Playwright はデフォルトでビューポートサイズを固定する。no_viewport=True を指定すると、ウインドウサイズに応じてビューポートが動的に変わるようになる。サイネージ用途では画面全体を使いたいのでこの設定が必要。

.env からの認証情報読み込み

パスワードなどの認証情報は .env ファイルに記載し、python-dotenv で読み込む。

USERNAME=your-username
PASSWORD=your-password

フルスクリーン化の試行錯誤

Raspberry Pi 上で Chromium をフルスクリーン表示するために、いくつかの方法を試した。

失敗した方法

1. xdotool key F11

シェルスクリプトから遅延実行で F11 キーを送る方法。

sleep 10 && xdotool key F11 &

タイミングの問題で安定しなかった。ブラウザが完全に起動する前に送信されたり、フォーカスが合わなかったりする。

2. --start-maximized

Chromium の起動オプション。ウインドウは最大化されるが、アドレスバーやタブバーは残る。サイネージ用途には不十分。

3. JavaScript requestFullscreen()

Playwright から JavaScript を実行してフルスクリーンにする方法。

page.evaluate("document.documentElement.requestFullscreen()")

ユーザーのジェスチャー (クリックなど) を伴わないプログラム的な呼び出しは、ブラウザのセキュリティポリシーによりブロックされる場合がある。

採用した方法: --kiosk

chromium_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 にリダイレクトしているので、問題が発生した場合はこのファイルを確認する。

Mac からラズパイへのデプロイ

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

まとめ

現在未評価
タイトルとURLをコピー
著者は、アプリケーション開発会社 Cyberneura を運営しています。
開発相談をお待ちしています。

アーカイブ

2026
2025
2024
2023
2022
2021
2020
2019
2018
2017
2016
2015
2014
2013
2012
2011