Alexa TTS API 実装記録 (2026-03-02)
+ +Alexa TTS API マスタードキュメント
+最終更新: 2026-03-03 状態: サーバーからの日本語TTS未解決(調査中)
+
------------
+
2026/03/03 10:24 akira記録
+
akiraが下記の変更をしましたので、内容を読んでください。
+
+
1) 構成とサーバーへのファイル受け渡し方法を変更しました
+
/home/claude/windmill_workflow
+
に、https://gitea.keinafarm.net/akira/windmill_workflow.gitをcloneしました
+
これにより、
+
C:\Users\akira\Develop\windmill_workflow
+
とのやり取りはgiteaを使って出来るようになります。
+
+
2) docker compose up -dで、「 Docker 内部の IP アドレスが変わり、Traefik のルーティングが古い IP
+ を参照したまま 502/504 エラーになる」のは、原因があるはずです。(他のコンテナで、問題になっていないので)
+
調査して、Traefik 再起動が不必要になるようにしたいです
+
+
+
+
------------
+
概要
+Windmill から家中の Echo デバイスに任意のテキストを読み上げさせる API サーバー。Node.js + Express で実装し、Docker + コンテナとして Windmill サーバー上で稼働する。
+⚠ 現在の問題: ローカル PC からは日本語TTS動作確認済み。しかしサーバー(keinafarm.net)のコンテナからリクエストすると日本語文字が + Amazon 側で除去されて発話されない。原因は Amazon の IP ベースフィルタリング(海外IPからは日本語 textToSpeak + を無視する模様)。調査継続中。
+ファイル構成
+| ファイル | +場所 | +役割 | +備考 | +
|---|---|---|---|
| server.js | +alexa-api/(リポジトリ) | +Express API サーバー本体 | +本番コード。変更したらビルド・再デプロイが必要 | +
| Dockerfile | +alexa-api/(リポジトリ) | +Docker イメージ定義 | +node:20-alpine ベース。server.js と package*.json をコピー | +
| docker-compose.yml | +alexa-api/(リポジトリ) | +コンテナ起動設定 | +windmill_windmill-internal ネットワーク接続。外部ポート非公開 | +
| package.json / package-lock.json | +alexa-api/(リポジトリ) | +npm 依存関係 | +本番: express のみ。devDeps に alexa-remote2(不使用) | +
| .env.example | +alexa-api/(リポジトリ) | +環境変数テンプレート | +ALEXA_COOKIE=xxx の形式 | +
| .env | +alexa-api/(リポジトリ、.gitignore 対象) | +実際の Cookie 保管 | +Git にコミットしない。ローカル作業後に scp でサーバーへ転送 | +
| auth4.js | +alexa-api/(リポジトリ) | +Amazon 認証・Cookie 取得スクリプト | +ローカルのみで実行(Windows PC) | +
| auth.js / auth2.js / auth3.js | +alexa-api/(リポジトリ) | +auth4.js の旧バージョン | +参考用。実際は auth4.js を使う | +
| test_tts.js | +alexa-api/(リポジトリ) | +ローカルテスト用スクリプト | +直接 alexa.amazon.co.jp を叩いて動作確認 | +
サーバー上のファイル場所: /home/claude/alexa-api/(git
+ リポジトリとは別にコピーして管理)
サーバーへのデプロイ手順
+server.js や Dockerfile、package.json を変更した場合は以下の手順でサーバーに反映する。
+Step 1: ローカルでファイルを編集
+リポジトリ(c:\Users\akira\Develop\windmill_workflow\alexa-api\)でファイルを編集する。
+Step 2: scp でサーバーに転送
+変更したファイルをサーバーに scp で転送する:
+# server.js を変更した場合 scp alexa-api/server.js keinafarm-claude:/home/claude/alexa-api/server.js + # Dockerfile や package.json を変更した場合 scp alexa-api/Dockerfile keinafarm-claude:/home/claude/alexa-api/Dockerfile + scp alexa-api/package.json keinafarm-claude:/home/claude/alexa-api/package.json + scp alexa-api/package-lock.json keinafarm-claude:/home/claude/alexa-api/package-lock.json + # .env を更新した場合(Cookie 更新時など) scp alexa-api/.env keinafarm-claude:/home/claude/alexa-api/.env
+Step 3: サーバーでビルドして再起動
+⚠ 重要: docker compose restart はイメージをリビルドしない。server.js
+ 等を変更した場合は必ず build + up -d を実行すること。
# SSH でサーバーに接続してビルド+起動 ssh keinafarm-claude cd /home/claude/alexa-api + # イメージをビルド(server.js 等の変更を反映) sudo docker compose build # コンテナを再作成して起動 + sudo docker compose up -d # Traefik を再起動(コンテナ再作成後は必須) sudo docker restart + traefik
+Step 4: 動作確認
+# ヘルスチェック(Windmill ワーカーコンテナ内から) curl http://alexa_api:3500/health # ログ確認 + sudo docker logs alexa_api -f # デバイス一覧確認 curl http://alexa_api:3500/devices
+Cookie だけ更新する場合(server.js 変更なし)
+# .env をサーバーに転送 scp alexa-api/.env keinafarm-claude:/home/claude/alexa-api/.env + # コンテナを再起動(restart で OK → イメージのリビルド不要) ssh keinafarm-claude 'sudo docker + compose -f /home/claude/alexa-api/docker-compose.yml restart' # ※ Traefik + の再起動は不要(コンテナ再作成しないため)
+Traefik 再起動が必要な理由
+docker compose up -d はコンテナを「再作成」する(docker compose restart は既存コンテナを再起動するだけ)。コンテナが再作成されると + Docker 内部の IP アドレスが変わり、Traefik のルーティングが古い IP を参照したまま 502/504 エラーになる。
+対処: sudo docker restart traefik で Traefik
+ に新しい IP を再検出させる。
この問題は Traefik の設定で watch: true にすれば自動解消できるが、現状はコンテナ再作成のたびに手動で
+ Traefik を再起動する運用としている。
docker-compose.yml の内容
+services: alexa-api: build: . container_name: + alexa_api restart: unless-stopped env_file: + - .env environment: - PORT=3500 + networks: - windmill_windmill-internal + # 外部には公開しない(Windmill ワーカーから内部ネットワーク経由でのみアクセス) # + デバッグ時は以下のコメントを外す: # ports: # - "127.0.0.1:3500:3500" + networks: windmill_windmill-internal: external: true
+認証方法(auth4.js)
+Amazon Japan OpenID フローを自前で実装。ローカル PC(Windows)でのみ実行する:
+# ローカルPC の alexa-api ディレクトリで実行 cd alexa-api AMAZON_EMAIL="メールアドレス" AMAZON_PASSWORD="パスワード" + node auth4.js
+成功すると alexa-api/.env が生成または更新される。
+ログインフローの概要:
+-
+
- GET https://www.amazon.co.jp/ap/signin?openid.assoc_handle=amzn_dp_project_dee_jp +
- hidden フィールド(anti-csrftoken-a2z, appActionToken, workflowState 等)を抽出 +
- POST でメール/パスワードを送信 +
- alexa.amazon.co.jp/api/apps/v1/token へのリダイレクトをたどる +
- 取得した Cookie(at-acbjp, sess-at-acbjp, sst-acbjp, session-token 等)を .env + に保存 +
TTS の仕組み(server.js)
+alexa-remote2 は使わない直接 API 実装。Endpoints:
+-
+
- POST /speak — { device: "デバイス名 or serial", text: "しゃべる内容" } +
- GET /devices — デバイス一覧 +
- GET /health — ヘルスチェック +
内部の API 呼び出し順序:
+-
+
- GET /api/language → Set-Cookie: csrf=XXXXX を取得(毎リクエストごと) +
- GET /api/bootstrap → customerId を取得(キャッシュ: 永続)(A1AE8HXD8IJ61L) +
- GET /api/devices-v2/device → デバイス一覧(5分キャッシュ) +
- POST /api/behaviors/preview にシーケンス JSON を送信 +
POST /api/behaviors/preview のボディ構造:
+{ behaviorId: "PREVIEW", sequenceJson: JSON.stringify({ + "@type": "com.amazon.alexa.behaviors.model.Sequence", startNode: + { "@type": "com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode", + type: "Alexa.Speak", operationPayload: + { deviceType: "...", deviceSerialNumber: + "...", customerId: "A1AE8HXD8IJ61L", + locale: "ja-JP", // ← 重要(下記参照) + textToSpeak: "発話内容" } } + }), status: "ENABLED" }
+ヘッダーに csrf: XXXXX と Cookie に csrf=XXXXX の両方が必要。Content-Length
+ は Buffer.byteLength で計算(マルチバイト文字対応)。
⚠ locale パラメータについて(重要・未解決)
+| locale 値 | +ローカル PC から | +サーバー(keinafarm.net)から | +
|---|---|---|
| ""(空文字) | +✅ 日本語・英語・漢字全て発話 | +❌ 英語TTSになり日本語部分が発話されない | +
| "ja-JP" | +❌ 一瞬音が出るだけ(失敗) | +❌ 日本語文字が Amazon 側で除去され英字のみ発話 | +
現在 server.js では locale: "ja-JP" に設定している。
仮説: Amazon が海外IP(keinafarm.net = 非日本IP)からのリクエストを IP ベースでフィルタリングし、textToSpeak + の日本語文字を除去している。Alexa.TextCommand は同じ問題がない(異なる API パス)。
+確認済み事実: alexa_api の server.js ログには日本語テキストが正しく届いている。除去は + Amazon サーバー側で発生。
+次の調査候補:
+-
+
- SSML の <lang xml:lang="ja-JP"> タグで強制的に日本語 TTS を指定できるか +
- Alexa.TextCommand で「次を読み上げて:{text}」形式が使えるか +
- ローカルブリッジ方式(ユーザーのローカル PC で小さなプロキシサーバーを動かし、クラウドサーバーからローカル経由で alexa.amazon.co.jp + を叩く) +
デバイス一覧(Echo デバイスのみ)
+| 名前 | +deviceType | +serialNumber | +
|---|---|---|
| プレハブ | +A4ZXE0RM7LQ7A | +G0922H085165007R | +
| リビングエコー1 | +ASQZWP4GPYUT7 | +G8M2DB08522600RL | +
| リビングエコー2 | +ASQZWP4GPYUT7 | +G8M2DB08522503WF | +
| オフィスの右エコー | +A4ZXE0RM7LQ7A | +G0922H08525302K5 | +
| オフィスの左エコー | +A4ZXE0RM7LQ7A | +G0922H08525302J9 | +
| 寝室のエコー | +ASQZWP4GPYUT7 | +G8M2HN08534302XH | +
Windmill スクリプト(u/admin/alexa_speak)
+export async function main(device: string, text: string) { const + res = await fetch("http://alexa_api:3500/speak", { method: + "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ device, text }), }); if (!res.ok) + throw new Error("alexa-api error " + res.status); return res.json(); + }
+device はデバイス名(日本語)またはシリアル番号で指定可能。Windmill ワーカーから http://alexa_api:3500 でアクセス(windmill_windmill-internal
+ ネットワーク経由)。
Cookie の更新手順
+Cookie は数日〜数週間で期限切れ。切れたら:
+# 1. ローカル PC で Cookie を取得 cd alexa-api AMAZON_EMAIL="メールアドレス" AMAZON_PASSWORD="パスワード" + node auth4.js # → alexa-api/.env が更新される # 2. サーバーに .env を転送 scp alexa-api/.env + keinafarm-claude:/home/claude/alexa-api/.env # 3. コンテナを再起動(restart で OK、リビルド不要) + ssh keinafarm-claude 'sudo docker compose -f /home/claude/alexa-api/docker-compose.yml + restart' # ※ Traefik 再起動は不要(コンテナ再作成なし)
+既知の問題・落とし穴
+-
+
- docker compose restart ≠ リビルド: server.js を変更しても restart + ではコンテナ内のコードは古いまま。build + up -d が必要。 +
- コンテナ再作成後は Traefik 再起動必須: up -d でコンテナ再作成すると Docker 内部
+ IP が変わり Traefik が 502/504 を返す。
sudo docker restart traefikで解消。
+ - alexa-remote2 は使えない: 取得した Cookie 文字列を受け付けない(内部で再認証しようとして失敗)。直接 + API 実装が必要。 +
- CSRF トークンは Cookie と ヘッダーの両方に必要: csrf ヘッダーだけ、または Cookie + だけでは認証失敗。 +
- operationPayload に customerId 必須: ないと 400 エラー。 +
- レート制限: 短時間に連続リクエストすると HTTP 429 または 200 で音が出ない。通常の通知用途では問題なし。 +
- git push がブロックされる: Gitea の pre-receive フック(
remote: Gitea: User permission denied for writing)で + push が失敗する。根本原因は未調査。ファイル転送は scp で行っている。
+ - firstRunCompleted: false はデバイス設定の未完了フラグ: TTS には直接影響しない(root + cause ではなかった)。 +
サーバー上の運用コマンド一覧
+# コンテナ状態確認 sudo docker ps | grep alexa # リアルタイムログ確認 sudo docker logs alexa_api + -f # コンテナ停止 sudo docker compose -f /home/claude/alexa-api/docker-compose.yml + stop # ビルド+起動(コード変更後) cd /home/claude/alexa-api sudo docker compose build + sudo docker compose up -d sudo docker restart traefik # Cookie 更新時(再起動のみ) + sudo docker compose -f /home/claude/alexa-api/docker-compose.yml restart
+