google-home-notifierとは?
https://qiita.com/SatoTakumi/items/c9de7ff27e5b70508066
google homeから自発的に喋ってもらうことができます
自分はMacだとできたのですが、タイトルのようにラズパイに入れるのに躓きました
https://qiita.com/vanx2/items/3c20bf8e4111da9eb68d
google-home-notifier内のmdnsが外部モジュール依存だそうです
ここらへんのモジュールのインストールが失敗していました
mdnsをピュアJSのmdns-jsに入れ替えるぞ!
ピュアjsなら環境に依存しないらしいぞ
今回入れた環境やバージョン
raspbian 8.0
node 9.3.0
npm 5.5.1
npmはできるだけ最新にしたいです
以下がnpm自体をアップデートするコマンドです
npm install -g npm
gitからクローン
gitからクローンのみ
まだインストールしない
git clone https://github.com/noelportugal/google-home-notifier
cd google-home-notifier
package.jsonの編集
vimにて編集する際のコマンド例
sudo vim package.json
21行目のmdnsをmdns-jsに変え、mdns-jsの最新のバージョンに変更
以下が編集後のファイル
{
"name": "google-home-notifier",
"version": "1.2.0",
"description": "",
"main": "google-home-notifier.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Noel Portugal",
"keywords": [
"google home",
"notifications",
"notifier"
],
"license": "MIT",
"dependencies": {
"body-parser": "^1.15.2",
"castv2-client": "^1.1.2",
"express": "^4.14.0",
"google-tts-api": "https://github.com/darrencruse/google-tts/tarball/british-voice",
"mdns-js": "^1.0.1",
"ngrok": "^2.2.4"
}
}
インストール開始
npm install
なぜかgoogle-tts-apiが見つからないとのエラーがでましたが、
npm updateでなぜか直りました
google-home-notifier.jsの編集
変更した箇所はmdnsの宣言あたりと、browserを使った処理あたりです
mdnsのstopのタイミングだけ変わって、少し心配
でもとりあえず動いています
編集前のタイミングはいちおうコメントで残しています
mdnsとmdns-jsはだいたいデータが同じなのですが、nameじゃなくて、fullnameを見るようにしたので、差分だけ編集したい人はそこだけ注意です
play関数の方はまだ試せていないです〜
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
// var mdns = require('mdns');
// var browser = mdns.createBrowser(mdns.tcp('googlecast'));
var mdns = require('mdns-js');
var browser = mdns.createBrowser(mdns.tcp('googlecast'));
var deviceAddress;
var language;
var device = function(name, lang = 'en') {
device = name;
language = lang;
return this;
};
var ip = function(ip) {
deviceAddress = ip;
return this;
}
var googletts = require('google-tts-api');
var googlettsaccent = 'us';
var accent = function(accent) {
googlettsaccent = accent;
return this;
}
var notify = function(message, callback) {
if (!deviceAddress){
browser.on('ready', function () {
browser.discover();
});
browser.on('update', function(service) {
console.log('Device "%s" at %s:%d', service.fullname, service.addresses[0], service.port);
if (service.fullname != undefined && service.fullname.includes(device.replace(' ', '-'))){
deviceAddress = service.addresses[0];
getSpeechUrl(message, deviceAddress, function(res) {
callback(res);
});
browser.stop();
}
//browser.stop();
});
}else {
getSpeechUrl(message, deviceAddress, function(res) {
callback(res);
});
}
};
var play = function(mp3_url, callback) {
if (!deviceAddress){
browser.on('ready', function () {
browser.discover();
});
browser.on('update', function(service) {
console.log('Device "%s" at %s:%d', service.fullname, service.addresses[0], service.port);
if (service.fullname.includes(device.replace(' ', '-'))){
deviceAddress = service.addresses[0];
getPlayUrl(mp3_url, deviceAddress, function(res) {
callback(res);
});
browser.stop();
}
// browser.stop();
});
}else {
getPlayUrl(mp3_url, deviceAddress, function(res) {
callback(res);
});
}
};
var getSpeechUrl = function(text, host, callback) {
googletts(text, language, 1).then(function (url) {
onDeviceUp(host, url, function(res){
callback(res)
});
}).catch(function (err) {
console.error(err.stack);
});
};
var getPlayUrl = function(url, host, callback) {
onDeviceUp(host, url, function(res){
callback(res)
});
};
var onDeviceUp = function(host, url, callback) {
var client = new Client();
client.connect(host, function() {
client.launch(DefaultMediaReceiver, function(err, player) {
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED' // or LIVE
};
player.load(media, { autoplay: true }, function(err, status) {
client.close();
callback('Device notified');
});
});
});
client.on('error', function(err) {
console.log('Error: %s', err.message);
client.close();
callback('error');
});
};
exports.ip = ip;
exports.device = device;
exports.accent = accent;
exports.notify = notify;
exports.play = play;
サンプルファイルの作成
これでいったん動作確認してみますか
同じフォルダ内にjsファイルを新規作成します
const googlehome = require('./google-home-notifier')
const language = 'ja';
googlehome.device('Google-Home', language);
googlehome.notify('こんにちは。私はグーグルホームです。', function(res) {
console.log(res);
});
そして実行
node app.js
どうでしょうか?話し始めましたか?
example.jsの編集
https://qiita.com/azipinsyan/items/db4606aaa51426ac8dac
こちらのサイトのまんまです
こちらを実行するとURLが生成され、そのURLにGETやPOSTを送ると喋らせることができます
しかも同一LAN内じゃなくても大丈夫です
var express = require('express');
var googlehome = require('./google-home-notifier');
var ngrok = require('ngrok');
var bodyParser = require('body-parser');
var app = express();
const serverPort = 8091; // default port
var deviceName = 'ribihome';
var ip = '192.168.1.10'; // default IP //ここにIPを記載
var urlencodedParser = bodyParser.urlencoded({ extended: false });
app.post('/google-home-notifier', urlencodedParser, function (req, res) {
if (!req.body) return res.sendStatus(400)
console.log(req.body);
var text = req.body.text;
if (req.query.ip) {
ip = req.query.ip;
}
var language = 'ja'; // default language code //ここに「ja」を記載
if (req.query.language) {
language;
}
googlehome.ip(ip, language);
googlehome.device(deviceName,language) //ここに命令文を追加
if (text){
try {
if (text.startsWith('http')){
var mp3_url = text;
googlehome.play(mp3_url, function(notifyRes) {
console.log(notifyRes);
res.send(deviceName + ' will play sound from url: ' + mp3_url + '\n');
});
} else {
googlehome.notify(text, function(notifyRes) {
console.log(notifyRes);
res.send(deviceName + ' will say: ' + text + '\n');
});
}
} catch(err) {
console.log(err);
res.sendStatus(500);
res.send(err);
}
}else{
res.send('Please GET "text=Hello Google Home"');
}
})
app.get('/google-home-notifier', function (req, res) {
console.log(req.query);
var text = req.query.text;
if (req.query.ip) {
ip = req.query.ip;
}
var language = 'ja'; // default language code //ここに「ja」を記載
if (req.query.language) {
language;
}
googlehome.ip(ip, language);
googlehome.device(deviceName,language) //ここに命令文を追加
if (text) {
try {
if (text.startsWith('http')){
var mp3_url = text;
googlehome.play(mp3_url, function(notifyRes) {
console.log(notifyRes);
res.send(deviceName + ' will play sound from url: ' + mp3_url + '\n');
});
} else {
googlehome.notify(text, function(notifyRes) {
console.log(notifyRes);
res.send(deviceName + ' will say: ' + text + '\n');
});
}
} catch(err) {
console.log(err);
res.sendStatus(500);
res.send(err);
}
}else{
res.send('Please GET "text=Hello+Google+Home"');
}
})
app.listen(serverPort, function () {
ngrok.connect(serverPort, function (err, url) {
console.log('Endpoints:');
console.log(' http://' + ip + ':' + serverPort + '/google-home-notifier');
console.log(' ' + url + '/google-home-notifier');
console.log('GET example:');
console.log('curl -X GET ' + url + '/google-home-notifier?text=Hello+Google+Home');
console.log('POST example:');
console.log('curl -X POST -d "text=Hello Google Home" ' + url + '/google-home-notifier');
});
})
ついでにforever入れるぞ
一回起動すると放置ができない
sshで接続していようが、そのターミナルの画面と閉じるときにはそのプロセスも閉じることになるはず
なので、nodeをバックグランドで起動しておくようにする
sudo npm install -g forever
forever start example.js
起動してもコンソールがでないのでURLがわからない!
でも大丈夫
forever list
ここのlogfile(この場合は/home/pi/.forever/8Or1.log)にコンソールのログもあります
なので
cat /home/pi/.forever/8Or1.log
で確認
ちなみにforeverで立ち上げたプロセスを止めるのもこの画面のuidを確認後
forever stop 8Or1
追記(2018 1/18)
nodeのバージョンが古いとインストールできないとのこと
今回は当時の最新のv9.3だったはずだけれども、もしかしたらsudoのほうのnodeがデフォルトのほうのnodeになっていたのかもしれない
以下を参考に、nodeのアンインストールから最新をインストールが確実かもしれない
https://qiita.com/ktetsuo/items/8c9cd5714e231aa6ae09
その他に、google-home-notifierのgithubのreadmeに予めインストールしておいてねっていう記述が見られる
https://github.com/noelportugal/google-home-notifier
Raspberry Pi
If you are running from Raspberry Pi make sure you have the following before nunning "npm install": Use the latest nodejs dist.
curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -
sudo apt-get install nodejs
Also install these packages:
sudo apt-get install git-core libnss-mdns libavahi-compat-libdnssd-dev
結構確認していたはずですが、色々抜けていました...
とりあえず、この記事はラズパイにgoogle-home-notifierを導入する一例ということで!