注意
この記事はAlexa-cookie(v3.0.3)向けの記事です。最新版(v3.4.1)をお使いの方は、以下を参照下さい。
ちなみに、Alexa-cookieのバージョンは以下で確認できます。
$cd ~/.node-red
$npm list|grep alexa-cookie
│ ├─┬ alexa-cookie2@3.2.1
はじめに
「Alexaをしゃべらせる(Node-red編)」や「Raspberry Piで最強の防犯カメラを作ってみる(動画記録・配信、動体検知・Line通知、顔検知・顔認証、Alexa搭載)[6/6]」でご紹介したように、我が家のスマートホームシステムでは、node-red-contrib-alexa-remote2を使って、Alexaをしゃべらせていました。
しかし、Alexa用のログインIDを新しいものに変えたところ、以下のようにログインができなくなってしまった😭 Node-redの画面には「Login unsuccessfull. Please check credentials.」というエラーが出ている。正常時は「Ready」。
原因:Amazon側の仕様変更
ネット上の情報を調査したところ、Amazon側の仕様変更によりユーザ&パスワードが動かなくなったとのこと。米国の「alexa.amazon.com」では、2019年夏頃から動かなくなったよう。。。日本の「alexa.amazon.co.jp」では、私の古いアカウントは使えていたので、アカウントによって、ログインできないアカウントがあるようです。
解決策:ログインをProxy認証に変更
私の調査結果では、node-red-contrib-alexa-remote2のログイン認証を「Email & Password」認証から「Proxy」認証に変更することで、解決できるらしい(後で分かるが、これだけでは解決できなかった)。
Alexa Accountノードの設定変更
Node-redのフロー上にある、Alexa Rutineノードをどれか一つ開きます。次に「Account」欄の鉛筆アイコンをクリックして「Alexa Accountノード」の設定画面を出し、以下のように設定します。
Auth Method:Proxy
This IP:※Node-redをインストールしているホストのIPアドレス
Port:3456 ※デフォルトでOK
Service Host:alexa.amazon.co.jp
Page:amazon.co.jp
Language:ja_JP
デプロイ
Proxy認証に変更したら「デプロイ」ボタンを押して、変更内容をデプロイしましょう。
デプロイが完了すると、Alexa Rutineノードの下に「Open 192.168.0.1:3456 in your browser」と表示されます。
ブラウザでアクセス
画面の指示通り、PCのWebブラウザで「http://192.168.0.1:3456/」にアクセスします。IPアドレスはnode-redホストのものに適宜変更してくださいね。すると、以下のようにAlexaのログイン画面が表示されるので、Amazonのユーザ名とパスワードを入力してログインします。ちなみに、2段階認証にしている人は、確認コードも入力が必要です。
ログイン
ログインが成功すると、以下の画面のようにCookieが取得できた旨が表示されます。
しかし、Node-redの画面に戻ってみると「Unexpected end of JSON input」とのエラーになってしまいます。。。ここからが長かった。。。。
Alexa-Cookie2のカスタマイズ
エラー原因を調べているとnode-red-contrib-alexa-remote2のCookie取得は、Alexa-cookie2というモジュールを利用していることが分かりました。
このモジュールのソースコードを読んでいくと、www.amazon.comにログインし、alexa.amazon.comからCookieを取得しているようです。日本語版のAlexaはalexa.amazon.co.jpでサービス提供されているので、alexa.amazon.comにはアカウントが無く、エラーとなっているようです。
そこで、このモジュールをamazon.co.jpに対応するようにカスタマイズして行きます。
alexa-cookie.jsの変更
alexa-cookie.jsに変更を加えていきます。
①alexa-cookie.jsを開く
Node-red起動ユーザのホームディレクトリの「.node-red/node_modules/node-red-contrib-alexa-remote2/node_modules/alexa-cookie2」にalexa-cookie.jsがあるので、これをバックアップ(コピー)して、開きます。
$cd ~/.node-red/node_modules/node-red-contrib-alexa-remote2/node_modules/alexa-cookie2
$cp alexa-cookie.js alexa-cookie.js.org
$vi alexa-cookie.js
②alexa-cookie.jsの変更
alexa-cookie.jsの494行目あたりに「host: ‘alexa.amazon.com’」の記載があるので、これを「host: ‘alexa.amazon.co.jp’」に変更します。
let options = {
//host: 'alexa.amazon.com', //ここを書き換える
host: 'alexa.amazon.co.jp', //ここを書き換える
path: '/api/users/me?platform=ios&version=2.2.223830.0',
method: 'GET',
headers: {
'User-Agent': 'AmazonWebView/Amazon Alexa/2.2.223830.0/iOS/11.4.1/iPhone',
'Accept-Language': _options.acceptLanguage,
'Accept-Charset': 'utf-8',
'Connection': 'keep-alive',
'Accept': 'application/json',
'Cookie': Cookie
}
};
proxy.jsの変更
次に、proxy.jsに変更を加えて行きます。
①proxy.jsを開く
alexa-cookie.jsがあるディレクトリ内の「lib」ディレクトリにproxy.jsがあるのでこれを開きます。
$cd lib
$cp proxy.js proxy.js.org
$vi proxy.js
②amazon.comの置換
proxy.js内にある「amazon.com」の記載を全て「amazon.co.jp」に変更します。結構数があるので、エディタの置換機能などを使うと良いと思います。
③alexa..amazon.co.jpの変更
proxy.jsの107行目にある「alexa..amazon.co.jp」(上で変更しているのでco.jpになってる)の記載を「alexa.amazon.co.jp」に変更(余分なドットを取る)します。これに気づくのに半日格闘しました💦
//optionsAlexa.pathRewrite[`^/alexa..amazon.co.jp`] = '';
optionsAlexa.pathRewrite[`^/alexa.amazon.co.jp`] = '';
④initialUrlの変更
www.amazon.comとwww.amazon.co.jpは少し仕様が違うようで、136行目あたりにある初期ページのURLを以下のように変更します。
//const initialUrl = `https://www.amazon.co.jp/ap/signin?openid.return_to=https%3A%2F%2Fwww.amazon.co.jp%2Fap%2Fmaplanding&openid.assoc_handle=amzn_dp_project_dee_ios&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&pageId=amzn_dp_project_dee_ios&accountStatusPolicy=P1&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns.oa2=http%3A%2F%2Fwww.amazon.co.jp%2Fap%2Fext%2Foauth%2F2&openid.oa2.client_id=device%3A${deviceId}&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&openid.oa2.response_type=token&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.pape.max_auth_age=0&openid.oa2.scope=device_auth_access&language=${_options.amazonPageProxyLanguage}`;
const initialUrl = `https://www.amazon.co.jp/ap/signin?showRmrMe=1&openid.return_to=https%3A%2F%2Falexa.amazon.co.jp%2F&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.assoc_handle=amzn_dp_project_dee_jp&openid.mode=checkid_setup&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&`;
⑤終了条件の変更
最後に、Cookie取得を判定する終了条件の変更を行います。proxy.jsの249行目に「/spa/index.html」の記述を追加して、このページが開かれたら終了するようにします。
if (
(proxyRes.socket && proxyRes.socket._host === `www.amazon.co.jp` && proxyRes.socket.parser.outgoing && proxyRes.socket.parser.out
going.method === 'GET' && proxyRes.socket.parser.outgoing.path.startsWith('/ap/maplanding')) ||
(proxyRes.socket && proxyRes.socket.parser.outgoing && proxyRes.socket.parser.outgoing._headers.location && proxyRes.socket.parse
r.outgoing._headers.location.includes('/ap/maplanding?')) ||
(proxyRes.headers.location && proxyRes.headers.location.includes('/ap/maplanding?'))
|| (proxyRes.headers.location && proxyRes.headers.location.includes('/spa/index.html')) //この一行を追加
) {
変更は以上です。念のため、Nod-redを再起動しておきましょう。
参考までに、proxy.jsのdiffも載せておきます。
87c87
< target: `https://alexa.amazon.com`,
---
> target: `https://alexa.amazon.co.jp`,
106,109c106,109
< optionsAlexa.pathRewrite[`^/www.amazon.com`] = '';
< optionsAlexa.pathRewrite[`^/alexa..amazon.com`] = '';
< optionsAlexa.cookieDomainRewrite[`.amazon.com`] = _options.proxyOwnIp;
< optionsAlexa.cookieDomainRewrite['amazon.com'] = _options.proxyOwnIp;
---
> optionsAlexa.pathRewrite[`^/www.amazon.co.jp`] = '';
> optionsAlexa.pathRewrite[`^/alexa.amazon.co.jp`] = '';
> optionsAlexa.cookieDomainRewrite[`.amazon.co.jp`] = _options.proxyOwnIp;
> optionsAlexa.cookieDomainRewrite['amazon.co.jp'] = _options.proxyOwnIp;
124,127c124,127
< if (url.startsWith(`/www.amazon.com/`)) {
< return `https://www.amazon.com`;
< } else if (url.startsWith(`/alexa.amazon.com/`)) {
< return `https://alexa.amazon.com`;
---
> if (url.startsWith(`/www.amazon.co.jp/`)) {
> return `https://www.amazon.co.jp`;
> } else if (url.startsWith(`/alexa.amazon.co.jp/`)) {
> return `https://alexa.amazon.co.jp`;
129,132c129,132
< if (req.headers.referer.startsWith(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.amazon.com/`)) {
< return `https://www.amazon.com`;
< } else if (req.headers.referer.startsWith(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.amazon.com/`)) {
< return `https://alexa.amazon.com`;
---
> if (req.headers.referer.startsWith(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.amazon.co.jp/`)) {
> return `https://www.amazon.co.jp`;
> } else if (req.headers.referer.startsWith(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.amazon.co.jp/`)) {
> return `https://alexa.amazon.co.jp`;
136c136,137
< const initialUrl = `https://www.amazon.com/ap/signin?openid.return_to=https%3A%2F%2Fwww.amazon.com%2Fap%2Fmaplanding&openid.assoc_handle=amzn_dp_project_dee_ios&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&pageId=amzn_dp_project_dee_ios&accountStatusPolicy=P1&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns.oa2=http%3A%2F%2Fwww.amazon.com%2Fap%2Fext%2Foauth%2F2&openid.oa2.client_id=device%3A${deviceId}&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&openid.oa2.response_type=token&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.pape.max_auth_age=0&openid.oa2.scope=device_auth_access&language=${_options.amazonPageProxyLanguage}`;
---
> //const initialUrl = `https://www.amazon.co.jp/ap/signin?openid.return_to=https%3A%2F%2Fwww.amazon.co.jp%2Fap%2Fmaplanding&openid.assoc_handle=amzn_dp_project_dee_ios&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&pageId=amzn_dp_project_dee_ios&accountStatusPolicy=P1&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.mode=checkid_setup&openid.ns.oa2=http%3A%2F%2Fwww.amazon.co.jp%2Fap%2Fext%2Foauth%2F2&openid.oa2.client_id=device%3A${deviceId}&openid.ns.pape=http%3A%2F%2Fspecs.openid.net%2Fextensions%2Fpape%2F1.0&openid.oa2.response_type=token&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.pape.max_auth_age=0&openid.oa2.scope=device_auth_access&language=${_options.amazonPageProxyLanguage}`;
> const initialUrl = `https://www.amazon.co.jp/ap/signin?showRmrMe=1&openid.return_to=https%3A%2F%2Falexa.amazon.co.jp%2F&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.assoc_handle=amzn_dp_project_dee_jp&openid.mode=checkid_setup&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&`;
141c142
< return `https://alexa.amazon.com`;
---
> return `https://alexa.amazon.co.jp`;
154,155c155,156
< const amazonRegex = new RegExp(`https?://www.amazon.com/`.replace(/\./g, "\\."), 'g');
< const alexaRegex = new RegExp(`https?://alexa.amazon.com/`.replace(/\./g, "\\."), 'g');
---
> const amazonRegex = new RegExp(`https?://www.amazon.co.jp/`.replace(/\./g, "\\."), 'g');
> const alexaRegex = new RegExp(`https?://alexa.amazon.co.jp/`.replace(/\./g, "\\."), 'g');
157,158c158,159
< data = data.replace(amazonRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/www.amazon.com/`);
< data = data.replace(alexaRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.amazon.com/`);
---
> data = data.replace(amazonRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/www.amazon.co.jp/`);
> data = data.replace(alexaRegex, `http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.amazon.co.jp/`);
164,167c165,168
< const amazonRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.amazon.com/`.replace(/\./g, "\\."), 'g');
< const alexaRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.amazon.com/`.replace(/\./g, "\\."), 'g');
< data = data.replace(amazonRegex, `https://www.amazon.com/`);
< data = data.replace(alexaRegex, `https://alexa.amazon.com/`);
---
> const amazonRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/www.amazon.co.jp/`.replace(/\./g, "\\."), 'g');
> const alexaRegex = new RegExp(`http://${_options.proxyOwnIp}:${_options.proxyPort}/alexa.amazon.co.jp/`.replace(/\./g, "\\."), 'g');
> data = data.replace(amazonRegex, `https://www.amazon.co.jp/`);
> data = data.replace(alexaRegex, `https://alexa.amazon.co.jp/`);
245c246
< (proxyRes.socket && proxyRes.socket._host === `www.amazon.com` && proxyRes.socket.parser.outgoing && proxyRes.socket.parser.outgoing.method === 'GET' && proxyRes.socket.parser.outgoing.path.startsWith('/ap/maplanding')) ||
---
> (proxyRes.socket && proxyRes.socket._host === `www.amazon.co.jp` && proxyRes.socket.parser.outgoing && proxyRes.socket.parser.outgoing.method === 'GET' && proxyRes.socket.parser.outgoing.path.startsWith('/ap/maplanding')) ||
247c248,249
< (proxyRes.headers.location && proxyRes.headers.location.includes('/ap/maplanding?'))
---
> (proxyRes.headers.location && proxyRes.headers.location.includes('/ap/maplanding?'))||
> (proxyRes.headers.location && proxyRes.headers.location.includes('/spa/index.html'))
Cookieの取得
それではNode-redの画面に戻りCookieを取得しましょう。
①「デプロイ」ボタンのクリック
「デプロイ」ボタンを再度クリックします。
②Alexaにログイン
Webブラウザで「http://192.168.0.1:3456/」にアクセスしてAlexaにログインします。
③ノードの確認
Node-redの画面に戻って確認しましょう。今度は、ノードの下に「Ready」と表示されてノードが使えるようになりました。試しにちゃんと動作するか確認してみてください。
おわりに
今回はAmazonの仕様変更によってログインエラーが出るようになってしまったnode-red-contrib-alexa-remote2のとりあえずの対処方をご紹介しました。半日程度はちゃんと動いていますが、何か問題があったら記事を更新したいと思います。
また、この記事で紹介した内容は、alexa-cookieの開発者の方にもGithubで連絡済みなので、日本向けに修正してくれるかもしれません。alexa-cookieの修正が入るまでは手動で対処しましょう〜😃
コメント
はじめまして。
大変勉強になる記事をいつもありがとうございます。
今回、私のalexaも喋らなくなってしまい、上記を参考にnode-redの設定を変更したいと思いましたが、
alexa-cookie.jsの場所がどうしても見つかりませんでした。
~/.node-red/node_modules/の下に node-red-contrib-alexa-remote2 が見当たりません。
こちらの記事の[リビングに飾れる美しいスマートホームコントローラ]のnode-redインストール手順で設定しています。
何かお分かりになることがあればお願いいたします。
コメントありがとうございます。node-red-contrib-alexa-remote2の場所ですが、node-redをroot起動しているため、以下にありませんでしょうか?
/root/.node-red/
上記に無い場合は、/home/root/.node-redに無いかもご確認ください。
早速にありがとうございます。
/home/root/.node-red/node_modulesの中にnode-red-contrib-alexa-remote2を見つける事ができました。
でもその下に/node_modulesがありませんでした。
ちなみに/home/root/.node-red/node_modulesの中に/alexa-cookie2/alexa-cookie.jsがあるのですが
こちらを記事のとうりに設定してもエラーになってしまいました。
私のraspiの環境(buster lite)がおかしいのかもしれませんね。
どのようなエラーになったか、教えていただければ分かるかもしれません。
ありがとうございます。
http://192.168.1.5:3456にアクセスしてログインすると、
続行するには、cookieを有効にしてください
Amazon.jpでのお買い物を続けるには、Webブラウザのcookieを有効にしてください。
ブラウザでcookieを有効にしたら、下にあるボタンをクリックして前のページに戻ってください。
となってしまいます。
ブラウザはedgeでcookieは有効になっています。chromeでも同じでした。
なるほど。。。初めてのケースですが、ブラウザを立ち上げwww.amazon.co.jpにアクセスしてログインしておき、
そのまま新しいタブを開いてhttp://192.168.1.5:3456にアクセスしたらいかがでしょうか?
ありがとうございます。
amazonにログインした状態でも同じ状況でした。
どうも私のレベルではこの辺が限界ですね。
大変参考になる記事を有難うございます。
2020年6月21日現在、Alexa-remote2は3.2.0にずいぶん大幅に書き換えられて、’amazon.com’の直書きから ${.options.baseAmazonPage}に修正されています。この修正と同時かどうかわかりませんが、alexa-cookie.jsとproxy.jsの場所も変わっているようです。しかしながらまだamazon.co.jpからのcookie取得は上手くいかないようで、Node-redのAlexa RoutineでUnexpected end of JSON inputのエラーで引っかかっております。
3.2.0対応の修正箇所をご教示いただけると幸いです。
ご連絡ありがとうございます。Alexa-remote2がバージョンアップしたんですね。alexa-cookeの開発者には連絡してあったので、修正してくれたのかもしれないです。ただ、amazon.co.jpからcookieが取得できないとのこと、次の週末に見てみますので、少々お待ち下さい。
はじめまして、記事大変参考になりました。
Alexa-remote2 v3.0.3近辺用ですよね?
私もv3.0.9などで試して駄目で、
v3.1.0にしたところpacificblueさんが仰っていた${.options.baseAmazonPage}に書き換えられておりましたので、
v3.0.3に戻してから、記載が無かったのですがalexa-cookie.jsのamazon.comもamazon.co.jpに置換しました。(ここを置換すれば、もしかしたらv3.0.9でもいけるかもですが試してません)
そしてデプロイの中のフローを再起動をすると繋がりました。
フローを再起動しないと/spa/index.htmlからリダイレクトせず完了しませんでした。
なおalexaが手元にない為しゃべったかの確認は取れておりません。
v3.2.1までバージョンUPしているようですが、この辺のバージョンでの対応方法もpacificblueさんと同じく知りたいと思っております。
お手数では御座いますがよろしくお願い致します。
Alexa-Cookie2のバージョンアップ(v3.2.1)に対応した修正方法を見つけました。以下の記事で紹介していますので、ご参照下さい。
https://www.smarthome-diy.info/blog/developper/smarthome/2020/06/1699/
度々申し訳ございません。
オプションのFile Pathを入力して取得できるファイルの中身がnullとの文字列なのでクッキー自体は保存出来てないと思われます。
はじめまして
いつも記事を参考にさせて頂いてます。
Alexa-remote2のamazon.co.jp対応についてですが、raspiを再起動した場合は
再度、Webブラウザで「http://192.168.0.1:3456/」にアクセスしてAlexaに
ログインが必要ですよね?
raspiを常時稼働させてますが、たまにRebootしないと調子悪いです。
crontabにて毎日定時Rebootしており、その際にchromedriverでブラウザを自動
操作してログインさせようとしてますが、上手くいかないです。
手動でchromedriverのブラウザ自動操作を走らせると、ログインできるのですが?
ご教示頂けると有難いです。よろしくお願いします。
ブログをお読みいただきありがとうございます。
chromedriverでの自動更新ですが、私は試したことがないのでわからないです。
当ブログの読者の方がCRONを使った自動化を行なっていますので、ご参考まで。
以下の記事の一番下のコメント欄にあります。
https://www.smarthome-diy.info/blog/developper/smarthome/2020/09/2049/