16
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ラズパイにgoogle-home-notifierを入れようとしたけどつまづいた

Last updated at Posted at 2017-12-26

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の最新のバージョンに変更
以下が編集後のファイル

package.json
{
  "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関数の方はまだ試せていないです〜

google-home-notifier.js
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ファイルを新規作成します

app.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内じゃなくても大丈夫です

example.js
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

スクリーンショット 2017-12-26 18.46.15.png

ここの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を導入する一例ということで!

16
30
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?