├── .editorconfig ├── index.js ├── .travis.yml ├── package.json ├── lib ├── search.js └── connect.js ├── LICENSE ├── .gitignore └── README.md /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const search = require('./lib/search'), 2 | Connecter = require('./lib/connect') 3 | 4 | module.exports = { search, Connecter } 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 7 4 | - 8 5 | - 9 6 | - node 7 | sudo: false 8 | addons: 9 | apt: 10 | packages: 11 | - libavahi-compat-libdnssd-dev 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-googlehome", 3 | "version": "0.0.7", 4 | "main": "index.js", 5 | "repository": "https://github.com/Ghostrick/node-googlehome", 6 | "author": "_Ghostrick_ ", 7 | "license": "MIT", 8 | "dependencies": { 9 | "castv2-client": "^1.2.0", 10 | "google-tts-api": "^0.0.4", 11 | "mdns": "^2.4.0", 12 | "mime-types": "^2.1.21" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/search.js: -------------------------------------------------------------------------------- 1 | const mdns = require('mdns'), 2 | browser = mdns.createBrowser(mdns.tcp('googlecast')) 3 | 4 | async function search(waitTime = 1000) { 5 | 6 | let services = [] 7 | 8 | browser.start() 9 | browser.on('serviceUp', service => { services.push(service) }) 10 | await new Promise(resolve => setTimeout(resolve, waitTime)) 11 | browser.stop() 12 | 13 | return services 14 | .filter(service => { if (service.txtRecord.md.match(/^Google Home/)) return service }) 15 | .map(service => { return Object.assign(service.txtRecord, {address: service.addresses[0]}) }) 16 | } 17 | 18 | module.exports = search 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ishizaka Tomoya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | yarn.lock 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (http://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # Typescript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | .env 60 | 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-googlehome 2 | It uses Node.js to communicate with Google Home. 3 | 4 | [![npm version](https://badge.fury.io/js/node-googlehome.svg)](https://badge.fury.io/js/node-googlehome) 5 | [![Build Status](https://travis-ci.org/Ghostrick/node-googlehome.svg?branch=master)](https://travis-ci.org/Ghostrick/node-googlehome) 6 | 7 | ## Install 8 | ### npm 9 | ```bash 10 | $ npm install node-googlehome --save 11 | ``` 12 | ### yarn 13 | ```bash 14 | $ yarn add node-googlehome 15 | ``` 16 | 17 | ## Usage 18 | ```js 19 | const GoogleHome = require('node-googlehome') 20 | 21 | /** 22 | * search google home. 23 | * @param {waitTime: number(default: 1000)} 24 | */ 25 | GoogleHome.search(1000) 26 | .then(resp => { console.log(resp) }) 27 | 28 | /** 29 | * connect google home with IP. 30 | * @param {ip_address: string} 31 | */ 32 | let device = new GoogleHome.Connecter('192.168.11.13') 33 | 34 | /** 35 | * setting language 36 | * @param {{lang: string(default: 'en')}} 37 | */ 38 | device.config({lang: 'ja'}) 39 | 40 | 41 | /** 42 | * Start speaker. 43 | * If you do this, google home will immediately sound when you call speak() or playMedia() 44 | */ 45 | device.readySpeaker() 46 | .then(() => { /* do something */ }) 47 | 48 | /** 49 | * play media. pass media url. 50 | * @param {url: string} 51 | */ 52 | device.playMedia('http://www.hypertrombones.jp/sample/system7.mp3') 53 | .then(console.log) 54 | .catch(console.log) 55 | 56 | /** 57 | * google home speak! 58 | * @param {message: string, speed: number(default:1), timeout: number(default: 3000)} 59 | */ 60 | device.speak('こんにちは!') 61 | .then(console.log) 62 | .catch(console.log) 63 | ``` 64 | 65 | ## Requirements 66 | ・Node.js >= v7 67 | 68 | -------------------------------------------------------------------------------- /lib/connect.js: -------------------------------------------------------------------------------- 1 | const googletts = require('google-tts-api'), 2 | Client = require('castv2-client').Client, 3 | DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver, 4 | mime = require('mime-types') 5 | 6 | 7 | const Connecter = (() => { 8 | const playMethod = Symbol('playMethod') 9 | 10 | class Connecter { 11 | 12 | constructor(ip){ 13 | this.ip = ip 14 | this.lang = 'en' 15 | this.player 16 | } 17 | 18 | config(options = {}) { 19 | this.lang = options.lang || this.lang 20 | } 21 | 22 | async readySpeaker() { 23 | const client = new Client() 24 | this.player = await new Promise((resolve, reject) => client.connect(this.ip, () => { 25 | client.launch(DefaultMediaReceiver, (err, player) => { 26 | err ? reject(err) : resolve(player) 27 | }) 28 | })) 29 | } 30 | 31 | async speak(message, speed = 1, timeout = 3000) { 32 | const url = await googletts(message, this.lang, speed, timeout) 33 | return await this[playMethod](url) 34 | } 35 | 36 | async playMedia(url) { 37 | if(!url.startsWith('http')) throw new Error('This format is not supported.') 38 | return await this[playMethod](url) 39 | } 40 | 41 | async [playMethod](url) { 42 | 43 | if(!this.player) { 44 | await this.readySpeaker() 45 | } 46 | 47 | const params = { 48 | contentId: url, 49 | contentType: mime.lookup(url) || 'audio/mp3', 50 | streamType: 'BUFFERED' 51 | } 52 | 53 | return await new Promise((resolve, reject) => { 54 | this.player.load(params, { autoplay: true }, (err, status) => { 55 | err ? reject() : resolve(status) 56 | }) 57 | }) 58 | 59 | } 60 | } 61 | 62 | return Connecter 63 | 64 | })() 65 | 66 | module.exports = Connecter 67 | --------------------------------------------------------------------------------