はじめに
前回、GoogleHome の goole-home-notifier を使った場合を書きましたが、Bluetoothスピーカー接続でやったらいいんじゃない?と思いやってみました。これなら AmazonEcho も同じBluetoothスピーカーなので。
構成
RaspberryPiで定刻になったらYahoo路線情報ページをスクレイピングして正常運行以外ならば Bluetooth接続されたスピーカーにしゃべってもらいます。音声は VoiceText を使用します。
ハード
- GoogleHome (私のはminiです)
- AmazonEcho (私のはDotです)
- RaspberryPi (今回は3B)
- 母艦(Windowsマシン。何でも良い)
ソフト
- raspbian (今回は 2017-09-07-raspbian-stretch を使いました)
- nodejs (v9.8.0)
- npm (v6.4.1)
- VoiceText
中心となるソフトウェアは上記です。
raspbian、nodejs、npm は設定済み前提です。
Bluetoothスピーカー
RaspberryPi に接続されているスピーカーがあればなんでも良いです。
GoogleHome/AmazonEcho が市民権を得ているご家庭が多くなってきたんではないでしょうか?
GoogleHome/AmazonEchoのBluetooth接続は以前に行いましたのでそちらを参考ください。
「GoogleHome/AmazonEco を RaspberryPi の Bluetooth スピーカーにする」
VoiceText Web API
Bluetooth スピーカーに出力する音声は「VoiceText」を使います
VoiceTextとはなんぞやというと、文字列を合成音に変えてくれるTTS(TextToSpeech)エンジンです。
VoiceText は無料ですがAPIキーが必要です。取得しましょう。
VoiceText Web API
メールアドレスだけで取得できます。APIキーを控えておいてください。
VoiceText のインストール
作業ディレクトリ(trainfo)を作成して npm でインストールします。
再生ライブラリは、node-aplay を使いますのでこれもインストールします。
$ mkdir ~/trainfo
$ cd ~/trainfo
$ npm install voicetext
$ npm install node-aplay
VoiceText のテスト
一旦ここで、VoiceText が動くかのテストを行います。
下記のスクリプトを作成します。<VoiceTextのAPIキー>は 「VoiceText Web API」にユーザ登録したときに取得したものです。
話者(voice.SPEAKER.HIKARI)は SHOW,HARUKA,HIKARI,TAKERU,SANTA,BEAR などが使えますよ。替えてみてください。
var VoiceText = require('voicetext');
var Sound = require('node-aplay');
var fs = require('fs');
var voice = new VoiceText('<VoiceTextのAPIキー>');
var OUT_PATH = '/home/pi/trainfo/_temp.wav'
voice
.speaker(voice.SPEAKER.HIKARI) // SHOW,HARUKA,HIKARI,TAKERU,SANTA,BEAR
.emotion(voice.EMOTION.HAPPINESS)
.emotion_level(voice.EMOTION_LEVEL.HIGH)
.volume(150)
.speak("こんにちは、ラズベリーパイのテストです", function (e, buf) {
if (e) {
console.error(e);
} else {
fs.writeFileSync(OUT_PATH, buf, 'binary');
new Sound(OUT_PATH).play();
console.log("write done.");
}
});
無事に声が聞こえてきたら成功です。次行きましょう。
Yahoo路線情報ページをスクレイピング
スクレイピングには cheerio-httpcli を使いますのでインストールします。
$ npm install cheerio-httpcli
下記のソースでは、3路線の情報を取得しています。適宜変更して使ってください。
「平常運転」のときは何もしゃべりませんので、まずは遅延があるところでテストしてみてください。
Yahoo路線情報(https://transit.yahoo.co.jp/traininfo/top)
const client = require('cheerio-httpcli');
const Sound = require('node-aplay');
var VoiceText = require('voicetext');
var fs = require('fs');
var voice = new VoiceText('<VoiceTextのAPIキー>');
const msg = [];
const OUT_PATH = '/home/pi/trainfo/_temp.wav'
client.fetch('https://transit.yahoo.co.jp/traininfo/detail/148/0/') // 江ノ島電鉄線
.then(function (result) {
check(result);
return client.fetch('https://transit.yahoo.co.jp/traininfo/detail/29/0/'); // 横須賀線
})
.then(function (result) {
check(result);
return client.fetch('https://transit.yahoo.co.jp/traininfo/detail/125/0/'); // 相鉄本線
})
.then(function (result) {
check(result);
})
.catch(function (err) {
console.log(err);
})
.finally(function () {
var text = msg.join();
if (text.length > 0) {
voice
.speaker(voice.SPEAKER.HIKARI)
.emotion(voice.EMOTION.HAPPINESS)
.emotion_level(voice.EMOTION_LEVEL.HIGH)
.volume(150)
.speak(text, function (e, buf) {
if (e) {
console.error(e);
} else {
fs.writeFileSync(OUT_PATH, buf, 'binary');
new Sound(OUT_PATH).play();
console.log("write done.");
}
});
}
});
function check(result) {
if (!result.error) {
var $ = result.$;
var ti = $('h1.title').text().trim();
var sv = $('#mdServiceStatus > dl > dt').text().trim();
var tr = $('#mdServiceStatus > dl > dd.trouble').text().trim();
if (!sv.match('平常運転')) {
msg.push(ti + tr);
}
console.log(ti);
console.log(sv);
console.log(tr);
}
}
実行
$ node trainfo-bt.js
江ノ島電鉄線
[!]列車遅延
混雑の影響で、列車に遅れが出ています。(11月23日 14時15分掲載)
横須賀線
[○]平常運転
相鉄本線
[○]平常運転
write done.
ちゃんと音声が出ましたでしょうか?
ここまでくればあともう少し!
※「平常運転」のときは、なにもしゃべりません。
cron
朝にしゃべってもらわないといけないのでスケジュールします。
$ crontab -l
#Bluetooth接続が切れているかもしれないので再接続
59 6 * * 1-5 /usr/local/bin/connect-bluetooth.sh
#平日7:00から15分ごとに運行情報プログラムを実行する
0,15,30 7 * * 1-5 node /home/pi/trainfo/trainfo-bt.js
以上です。