All posts Home Automation

Google Home Message Broadcast System [Node.js]

Build a household notification system using Google Home Mini devices and Node.js — convert text to speech via the Google TTS API and broadcast it to multiple rooms simultaneously.

Alexander Sigler 5 min read
NodeJSJavascript
Google Home Message Broadcast System [Node.js]

Introduction

If you are a tech nerd like me, you just have collected random devices as you go on walking through life. One of my favorites is called a Google Home Mini. I got these bad boys at BestBuy for around $35 during black Friday and have one set up in 3 different rooms.

The best part of this is that I can set timers, reminders, or ask what the weather is when I’m too lazy to check. With my house slowly moving towards home customization, one of the features I wanted was a household notification system. Turns out I have a Google Home Mini that can do just that… broadcast a custom message.

Prerequisites

  • Node.js
    • castv2-client
    • google-tts-api
  • Google Home / Google Home Mini
  • Static IP assigned to each given device

Step 1: Installing & setup of project

This one should be a breeze. If you are using straight up command line use NPM to install the packages. I currently use WebStorm IDE from JetBrains to write all my Node.JS code but all of this tutorial will be done assuming you use good old fashioned notepad.exe (plz don’t actually).

Create a new directory in your file system and open up your command line/terminal, then run the following commands.

cd /path/to/directory
npm init
npm install castv2-client
npm install google-tts-api

The above commands will create a new project with the installed libraries that you will need. Once your project has been initialized create a JS file in that directory and open it — you can name it whatever you’d like but I named mine GoogleHomeBroadcaster.js.

Step 2: Writing the code

Full Code

var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');

var App = {
    playin: false,
    DeviceIp: "",
    Player: null,
    GoogleHome: function (host, url) {
        var client = new Client();
        client.connect(host, function () {
            client.launch(DefaultMediaReceiver, function (err, player) {
                client.setVolume({ level: 1 }, function(err, volume) {
                  if (err) {
                    console.log('Error on setVolume:', err);
                  } else {
                    console.log('Volume set to ' + volume)
                  }
                });
                var media = {
                    contentId: url,
                    contentType: 'audio/mp3',
                    streamType: 'BUFFERED'
                };
                App.Player = player;
                App.Player.load(media, { autoplay: true }, function (err, status) {
                    App.Player.on('status', function (status) {
                        if (status.playerState === "IDLE" && App.playin === false) {
                            client.close();
                        }
                    });
                });
            });
        });
        client.on('error', function (err) {
            console.log('Error: %s', err.message);
            client.close();
        });
    },
    run: function (ip, text) {
        App.DeviceIp = ip;
        const url = googleTTS.getAudioUrl(text, {
            lang: 'en-US',
            slow: false,
            host: 'https://translate.google.com',
        });
        App.GoogleHome(App.DeviceIp, url, function (res) {
            console.log(res);
        })
    },
    broadcast: function(text){
        // From config, e.g. 192.168.68.105,192.168.68.107,192.168.68.124
        const ips = process.env.GOOGLEHOME_IPS.split(",");
        for (var s of ips){
            App.run(s, text);
        }
    }
};

App.run("192.168.68.124", "Test message sent to one device");
// App.broadcast("Broadcasted to all of the devices");

Code explanation

Imports and dependencies:

var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
  • castv2-client — A Chromecast client based on the CASTV2 protocol. This is how we communicate to our Google Home / Google Home Mini.
  • google-tts-api — An API from Google that converts our plaintext string into an mp3 file that gets played on our Google Home Mini.
  • Client — This object allows us to control our Google Home, referencing the speaker itself so we can alter things like volume.
  • DefaultMediaReceiver — The default client provided by the Google API. Handles basic audio streaming.

The GoogleHome function:

GoogleHome: function (host, url) {
    var client = new Client();
    client.connect(host, function () {
        client.launch(DefaultMediaReceiver, function (err, player) {
            client.setVolume({ level: 1 });
            var media = {
                contentId: url,
                contentType: 'audio/mp3',
                streamType: 'BUFFERED'
            };
            App.Player = player;
            App.Player.load(media, { autoplay: true }, function (err, status) {
                App.Player.on('status', function (status) {
                    if (status.playerState === "IDLE" && App.playin === false) {
                        client.close();
                    }
                });
            });
        });
    });
    client.on('error', function (err) {
        console.log('Error: %s', err.message);
        client.close();
    });
},

This takes a host (the Google Home’s static IP) and a url (our TTS audio URL). We connect to the client and launch the DefaultMediaReceiver, which notifies the Google Home that it’s going to receive media. Volume is set to 1 (max — 0.0 to 1.0 acts as a percentage). We construct a media object with content type audio/mp3, load it with autoplay, and close the connection when playback goes IDLE.

The run function:

run: function (ip, text) {
    App.DeviceIp = ip;
    const url = googleTTS.getAudioUrl(text, {
        lang: 'en-US',
        slow: false,
        host: 'https://translate.google.com',
    });
    App.GoogleHome(App.DeviceIp, url, function (res) {
        console.log(res);
    })
},

Entry point for a single device. googleTTS.getAudioUrl() wraps Google’s TTS service and returns something like:

https://translate.google.com/translate_tts?ie=UTF-8&q=Hello+this+is+a+test&tl=en&total=1&idx=0&textlen=24&client=tw-ob&prev=input&ttspeed=1

The broadcast function:

broadcast: function(text){
    const ips = process.env.GOOGLEHOME_IPS.split(",");
    for (var s of ips){
        App.run(s, text);
    }
}

A simple loop that iterates over a comma-separated list of IPs from the environment and broadcasts the message to all of them simultaneously.

Step 3: Conclusion

Overall this is a simple solution to having information broadcasted around your home.

The reason I went down this route was that I upgraded my doorbell to a Ring doorbell, but the indoor doorbell system wouldn’t interface with it correctly (it is really old and finicky) so I needed a way to let the house know someone was outside.

Henceforth the broadcast system using my Google Home Minis. The best part is that since it can take any text, I change it up for each month — during February I’ll have it say “Cupid is here to drop off some love, go say hi” or during the Christmas season “Santa is at the door, go check it out before he flies away”.

It’s also really easy to get this working with music (any .mp3 file with a URL), so swapping doorbell rings for custom songs is trivial!