LINE Bot × Google Home で家族のメッセージを音声通知

Raspberry Pi

「帰宅前に LINE でメッセージを送るだけで、Google Home が家族に知らせてくれる」——そんな仕組みを我が家に導入しました。
家族 LINE のグループや個人チャットにメッセージを送ると、LINE Bot が Webhook でラズパイに通知し、Google Home がリビングや子供部屋で「〇〇 by △△さん」と読み上げます。
本記事では、LINE Bot Webhook + Flask サーバーを使って LINE メッセージを Google Home に音声通知する実装方法を解説します。

なぜこの機能を作ったのか?

家族はそれぞれスマートフォンを持っていますが、テーブルに置きっぱなしにしていたり、音量を絞っていたりと、LINE の通知音に気づかないことがよくありました。
特に「もうすぐ帰るよ」「夕飯できたよ」「お風呂が沸いたよ」のような短いメッセージは、スマホを手に取らずに家族全員に届いてほしいものです。
そこで「LINE に送るだけで家中の Google Home に声が届く」仕組みを作り、スマホを見ない家族にも確実に情報が伝わるようにしました。

LINE に送るだけで Google Home が家族に声で知らせてくれる!スマホを見なくても伝わる!

実現したいこと

  • 家族 LINE のグループや個人チャットへのメッセージをラズパイが受信し、Google Home(リビング)で自動読み上げ
  • 子供部屋専用の LINE チャンネルを作り、子供部屋の Google Home で最大音量・ランダム音声で読み上げ
  • メッセージ送信者の名前を自動的に末尾に付けて「誰からのメッセージか」を明示(例:「もうすぐ帰るよ by パパ」)
  • URL が含まれるメッセージは「ウェブページへのリンクあり」と短縮して読み上げ
  • LINE からスラッシュコマンドで Google Home の音量をリモート調整

この記事でわかること

  • LINE Bot SDK(linebot)で Webhook を受信し Flask Blueprint で処理する方法
  • 複数の LINE チャンネルを一つの Flask サーバーで同時に処理するパターン
  • 受信メッセージから送信者名・音声名を settings.json から引き当てる方法
  • URL を含むメッセージを自動的に短い説明文に置き換えて読み上げる方法
  • LINE からスラッシュコマンドで Google Home の音量・ミュートをリモート制御する方法
  • 子供部屋専用チャンネルで最大音量・ランダム音声を実現する方法

必要な準備と用意するもの

ハードウェア
  • Raspberry Pi(例:Raspberry Pi 5)
  • Google Home / Google Nest(リビング用・子供部屋用など)
ソフトウェア/サービス
  • Python 3
    • flask
    • line-bot-sdk(linebot)
    • googlehome(自作ライブラリ)
  • LINE Developers
    • LINE Bot チャンネル(リビング用・子供部屋用など、用途別に作成)
    • チャンネルアクセストークン・チャンネルシークレット
  • 外部公開 URL(LINE Webhook 受信用。ngrok や自宅サーバーの HTTPS 公開など)

完成イメージ

完成後のシステムは下記のように動作します。

  1. LINE → Google Home 読み上げ(リビング用):家族 LINE のグループや個人チャットにメッセージを送ると、LINE Bot が Webhook でラズパイに通知 → Google Home が「〇〇 by △△さん」と読み上げる
  2. LINE → Google Home 読み上げ(子供部屋用):子供部屋専用 LINE Bot にメッセージを送ると、子供部屋の Google Home が最大音量・ランダム音声で読み上げる
  3. スラッシュコマンド:LINE に「/volume 0.5」や「/mute」と送ると、Google Home の音量をリモートで変更できる

システムの仕組み

LINE Bot にメッセージが送信されると、LINE プラットフォームから Webhook(HTTP POST)がラズパイの Flask サーバーに届きます。
Flask Blueprint で各チャンネルのルートを定義し、LINE Bot SDK の WebhookHandler で署名検証と処理振り分けを行います。

チャンネルは用途ごとに複数作成しており、それぞれ異なるルートに Webhook を設定します。

  • /googlehome/:リビングの Google Home で読み上げ(家族グループ LINE や個人チャット用)
  • /googlehome_<子供の名前>/:子供部屋の Google Home で読み上げ(最大音量・ランダム音声)

Google Home への発話は自作ライブラリ「googlehome」を使用しています。実装の詳細は別記事を参照してください。

実装のポイント

複数チャンネルを一つの Flask サーバーで処理

LINE Bot のチャンネルを用途別に分けると、それぞれの Webhook URL が必要です。しかし複数の Flask サーバーを立ち上げるのは非効率なため、一つの Flask アプリに Blueprint でルートをまとめ、チャンネルごとの WebhookHandler を使い分けます。

チャンネル分離で柔軟な制御が可能
リビング用チャンネルと子供部屋用チャンネルを分けることで、音量・音声・動作をチャンネルごとに独立して設定できます。家族のグループ LINE とは別に子供部屋専用の LINE Bot を作ることで、子供への呼びかけ専用スピーカーとして活用できます。

送信者名の自動付与

settings.json に LINE ユーザー ID と名前・音声名のマッピングを定義しておくことで、誰からのメッセージかを自動的に識別します。メッセージ末尾に「by △△」を付加することで、Google Home が読み上げた際に誰からの連絡かすぐわかります。

例:パパが「もうすぐ帰るよ」と送ると → Google Home が「もうすぐ帰るよ by パパ」と読み上げる

URL の自動置換

LINE メッセージに URL が含まれると、Google Home が URL をそのまま読み上げてしまい非常に聞きづらくなります。そこでメッセージ受信時に URL を検出し、自動的に短い説明文に置き換えます。

  • メッセージ全体が URL → 「ウェブページへのリンクが送信されました。」
  • URL を含むテキスト → URL 部分を「[ウェブページへのリンクあり]」に置換して読み上げ

スラッシュコマンドで音量をリモート制御

メッセージが「/」から始まる場合はコマンドとして解釈し、Google Home のパラメータを変更します。音量調整やミュートを LINE から遠隔で操作できるため、外出先から「帰宅前に音量を上げておく」などの使い方ができます。

  • /volume 0.7:音量を 0.7 に設定(0.0〜1.0)
  • /up_volume:音量を上げる
  • /down_volume:音量を下げる
  • /mute:ミュート
  • /unmute:ミュート解除
  • /status:現在の状態(音量・IP・再生状態など)を LINE に返信
  • /help:コマンド一覧を LINE に返信

子供部屋チャンネルの最大音量・ランダム音声

子供部屋用チャンネルのメッセージハンドラでは、毎回音量を 1.0(最大)・volume_gain_db を 16.0 に設定してから読み上げます。音量が最大でない状態でも次のメッセージ受信時に自動的に最大音量に戻るため、誰かがコマンドで音量を下げていても確実に子供に届きます。
また googlehome.get_random_voice() で毎回異なる音声を選ぶことで、単調さを避けて聞き取りやすくしています。

子供が勉強や遊びに集中していても気づけるよう、爆音設定にしています。夜間は親が音量コマンドで下げることもできます。

事前準備

LINE Bot チャンネルの作成

LINE Developers コンソール(https://developers.line.biz/)でプロバイダーと Messaging API チャンネルを作成します。
用途ごとにチャンネルを分けて作成し、それぞれのチャンネルシークレットとチャンネルアクセストークンを取得してください。

  1. LINE Developers にログイン
  2. プロバイダーを作成(または既存のものを選択)
  3. 「Messaging API」チャンネルを作成
  4. 「チャンネル基本設定」からチャンネルシークレットを取得
  5. 「Messaging API 設定」からチャンネルアクセストークン(長期)を発行
  6. 「Messaging API 設定」の Webhook URL にラズパイのエンドポイントを登録(例:https://example.com/webhooks/googlehome/
  7. 「Webhook の利用」を ON にする
  8. LINE Bot をグループチャットに招待する(グループ通知の場合)

LINE Webhook は HTTPS が必須です。ラズパイを自宅サーバーとして公開する場合は、Let’s Encrypt 等で SSL 証明書を取得するか、ngrok などのトンネリングサービスを利用してください。

LINE ユーザー ID の取得

送信者名を自動付与するために、家族それぞれの LINE ユーザー ID を事前に取得しておく必要があります。
ユーザー ID は LINE Bot に話しかけた際の Webhook イベントの event.source.user_id から確認できます。Flask サーバーを起動してメッセージを送り、ログに出力して確認するのが手軽です。

ライブラリのインストール

pip install flask line-bot-sdk

実装方法

設定ファイル(settings.json)

LINE チャンネルのシークレット・アクセストークンと、家族それぞれのユーザー情報を JSON で管理します。

{
    "line": {
        "user": {
            "user1": {
                "id": "<LINEユーザーID>",
                "name": "<名前>",
                "voice_name": "ja-JP-Wavenet-A"
            },
            "user2": {
                "id": "<LINEユーザーID>",
                "name": "<名前>",
                "voice_name": "ja-JP-Wavenet-B"
            }
        },
        "channel": {
            "googlehome": {
                "channel_secret": "<チャンネルシークレット>",
                "access_token": "<アクセストークン>"
            },
            "googlehome_<子供の名前>": {
                "channel_secret": "<チャンネルシークレット>",
                "access_token": "<アクセストークン>"
            }
        }
    }
}

voice_name は Google Cloud Text-to-Speech の音声名です。家族ごとに異なる音声を設定することで、Google Home の声で誰からのメッセージかを聞き分けることもできます。

LINE Webhook サーバー(dispacher.py)

Flask Blueprint を使って LINE Webhook を受け取る処理を実装します。チャンネルごとに WebhookHandler を作成し、受信したメッセージを Google Home に発話させます。

# -*- coding: utf-8 -*-
import googlehome
from flask import request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage
from . import webhooks_bp

notify = googlehome.Notify(googlehome.GoogleHome.LIVING)
notify_childroom = googlehome.Notify(googlehome.GoogleHome.CHILD_ROOM)

handler_googlehome = WebhookHandler(settings['line']['channel']['googlehome']['channel_secret'])
handler_googlehome_child = WebhookHandler(settings['line']['channel']['googlehome_<子供の名前>']['channel_secret'])

# ---- リビング用チャンネルの Webhook エンドポイント ----
@webhooks_bp.route("/googlehome/", methods=['POST'])
def callback_googlehome():
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)
    try:
        handler_googlehome.handle(body, signature)
    except InvalidSignatureError:
        abort(400)
    return 'OK'

# ---- 子供部屋用チャンネルの Webhook エンドポイント ----
@webhooks_bp.route("/googlehome_<子供の名前>/", methods=['POST'])
def callback_googlehome_child():
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)
    try:
        handler_googlehome_child.handle(body, signature)
    except InvalidSignatureError:
        abort(400)
    return 'OK'

# ---- 共通:送信者情報の取得とメッセージ前処理 ----
def get_line_sending_info(event, line_bot_api: LineBotApi) -> tuple:
    userid = event.source.user_id
    try:
        user_name = line_bot_api.get_profile(userid).display_name
    except Exception:
        user_name = 'アノニマス'

    # settings.json から送信者名と音声名を取得
    sender_name = None
    voice_name = None
    for user in settings['line']['user']:
        if userid == settings['line']['user'][user]['id']:
            sender_name = settings['line']['user'][user]['name']
            voice_name = settings['line']['user'][user]['voice_name']
            break

    message = event.message.text
    # URL を含む場合は短い説明文に置き換え
    if message.startswith("http"):
        message = "ウェブページへのリンクが送信されました。"
    elif "http" in message:
        message = message.split("http")[0] + "[ウェブページへのリンクあり]"

    line_bot_name = line_bot_api.get_bot_info().display_name
    return line_bot_name, sender_name, voice_name, message

# ---- スラッシュコマンドの処理 ----
def set_googlehome_parameters(notify: googlehome.Notify, message: str) -> str | None:
    if not message.startswith("/") or len(message) < 2:
        return
    parts = message.strip().split()
    command = parts[0][1:]  # "/" を除去
    args = parts[1:]

    if command == "volume":
        notify.volume = float(args[0])
    elif command == "up_volume":
        notify.up_volume()
    elif command == "down_volume":
        notify.down_volume()
    elif command == "mute":
        notify.mute()
    elif command == "unmute":
        notify.unmute()
    elif command == "status":
        status = notify.status
        return (
            f'name: {status.get("name")}\n'
            f'ip: {status.get("ip")}\n'
            f'Volume: {status.get("volume")}\n'
            f'tts_voice_name: {notify.tts_voice_name}'
        )
    elif command == "help":
        return (
            '/volume: 音量を変更します (0.0 - 1.0)\n'
            '/up_volume: 音量を上げます\n'
            '/down_volume: 音量を下げます\n'
            '/mute: ミュートします\n'
            '/unmute: ミュートを解除します\n'
            '/status: ステータスを表示します\n'
            '/help: ヘルプを表示します'
        )

# ---- リビング用メッセージハンドラ ----
@handler_googlehome.add(MessageEvent, message=TextMessage)
def handle_message_googlehome(event):
    line_bot_api = LineBotApi(settings['line']['channel']['googlehome']['access_token'])
    line_bot_name, sender_name, voice_name, message = get_line_sending_info(event, line_bot_api)

    if message.startswith("/"):
        reply_message = set_googlehome_parameters(notify, message)
        if reply_message:
            line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_message))
    else:
        message = message.strip()
        if sender_name is not None:
            message = message + ' by ' + sender_name
        notify.speak(message, voice_name=voice_name)

# ---- 子供部屋用メッセージハンドラ(最大音量・ランダム音声)----
@handler_googlehome_child.add(MessageEvent, message=TextMessage)
def handle_message_googlehome_child(event):
    line_bot_api = LineBotApi(settings['line']['channel']['googlehome_<子供の名前>']['access_token'])
    line_bot_name, sender_name, voice_name, message = get_line_sending_info(event, line_bot_api)

    if message.startswith("/"):
        reply_message = set_googlehome_parameters(notify_childroom, message)
        if reply_message:
            line_bot_api.reply_message(event.reply_token, TextSendMessage(text=reply_message))
    else:
        # 毎回音量を最大にする
        if notify_childroom.volume < 1.0:
            notify_childroom.volume = 1.0
            notify_childroom.volume_gain_db = 16.0
        # 声を毎回ランダムに変える
        voice_name = googlehome.get_random_voice()
        notify_childroom.speak(message, None, voice_name)

systemd への登録

Flask サーバー(dispacher.py を含む Web サーバー)はラズパイ起動時に自動起動するよう systemd サービスとして登録します。

[Unit]
Description=Smarthome Flask Server
After=network.target

[Service]
ExecStart=/usr/bin/python3 /home/<username>/smarthome/server.py
Restart=always
User=<username>

[Install]
WantedBy=multi-user.target
sudo systemctl enable smarthome_server.service
sudo systemctl start smarthome_server.service

コードの解説

Webhook 署名の検証(セキュリティ)

LINE SDK の WebhookHandlerX-Line-Signature ヘッダーを自動的に検証します。チャンネルシークレットで HMAC-SHA256 署名を検証するため、第三者からの不正なリクエストを弾くことができます。
署名検証に失敗した場合は InvalidSignatureError がスローされ、abort(400) でリクエストを拒否します。

チャンネルシークレットとアクセストークンは絶対にソースコードにハードコードせず、settings.json や環境変数で管理してください。

送信者名の解決ロジック

get_line_sending_info() では、イベントの user_id を settings.json のユーザーリストと照合します。
一致するエントリが見つかれば登録済みの名前と音声名を使用し、見つからなければ sender_name = None となるため、「by △△」の付加をスキップします。家族以外の人が Bot にメッセージを送った場合でも正常に動作します。

グループトークと個人トークの両対応

LINE Bot はグループチャットに招待することも、個人チャットで利用することもできます。
グループトーク内では Bot 宛のメッセージだけでなくグループ内の全メッセージが Webhook として届くため、不要なメッセージが読み上げられる場合があります。家族のみのグループであれば全メッセージ読み上げで問題ありませんが、大人数のグループに Bot を入れる場合は送信者 ID によるフィルタリングを検討してください。

グループトークで Bot を追加すると「応答メッセージ」が自動で送信される場合があります。LINE Developers の「応答設定」で「応答メッセージ」をオフ、「Webhook」をオンに設定してください。

スラッシュコマンドの拡張

set_googlehome_parameters() に条件分岐を追加するだけでコマンドを自由に増やせます。例えば「/speak おはよう」で任意のメッセージを読み上げさせたり、「/play」で特定の音声ファイルを再生させたりといった拡張が可能です。

動作確認

  1. Flask サーバーを起動する
  2. LINE Developers の Webhook URL にラズパイのエンドポイントを登録し「検証」ボタンを押す → 200 OK が返ることを確認
  3. LINE Bot をグループに招待し(または個人チャットで)、メッセージを送信する → リビングの Google Home がメッセージを読み上げることを確認
  4. メッセージ末尾に「by △△」が付いていることを確認(settings.json にユーザー登録済みの場合)
  5. 「/help」と送信し、LINE にコマンド一覧が返信されることを確認
  6. 「/volume 0.3」と送信し、音量が変わることを確認
  7. URL を含むメッセージを送り、「ウェブページへのリンクあり」に置換されて読み上げられることを確認

LINE Developers の「検証」ボタンで 200 OK が返らない場合は、ラズパイ側のサーバーが起動しているか、外部公開 URL が正しく設定されているか、HTTPS 証明書が有効かを確認してください。

まとめ

LINE Bot Webhook と Flask を組み合わせることで、家族 LINE のメッセージを Google Home が自動で読み上げるシステムが実現できました。

  • Flask Blueprint で複数の LINE チャンネルを一つのサーバーで処理できる
  • settings.json でユーザーごとの名前・音声を管理し、送信者名を自動付与できる
  • URL 自動置換で聞き取りづらい読み上げを防げる
  • スラッシュコマンドで LINE から Google Home の音量をリモート制御できる
  • チャンネルを分けることで、リビング用・子供部屋用と用途別に動作を変えられる

「帰る前に LINE で一言送るだけで、家族全員が Google Home の声で気づいてくれる」——そんな小さな仕組みが日常のコミュニケーションをスムーズにします。ぜひ試してみてください。

タイトルとURLをコピーしました