Using Amazon Dash buttons to make Google Home say something

I've been spending some time researching the confusing world of home automation and in particular smart lighting. In the UK it is in a rather confused state - lots of bulbs with nifty features, but quite poor human interface usability, because of the complete lack of compatible available retrofit solutions for light fittings that can't have their bulbs changed (or use a large number of bulbs), and existing UK switch fittings and wiring. But that's another story...

However during this process I came across the Amazon Dash buttons again. I'd always thought one day these would make great simply home automation/IoT trigger devices. Anyway, thanks to the cost efficiency, I decided to dive in and buy 5 of the buttons. Amazon sells them for £5, but at present there's always one on Lightning deal for £2.50, which also gives £5.00 Amazon Pantry credit. I discovered this credit could be used against more Dash buttons, and also other items (I got £5 off a Sonicare toothbrush Lightning deal).

The Task

Although I plan to put them to use on lighting-related tasks, I decided to initially let the family have a bit of fun with them. My son and daughter both love Google Home, and I suspect my wife grudingly and secretly is impressed too. She had joked that we always have to say the same thing over again to the kids at the dinner table, so it'd be amusing to set the buttons up to have Google Home say a pre-programmed phrase:

Eat your dinner!

Sit up straight!

Accomplishing this involves two main things:

  1. Capturing the button press
  2. Triggering Google Home to speak

Well turns out it's pretty simple to do the first, if you have access to a home server, thanks to the work of others. There are Python (and I believe NodeJS) daemons that will listen for ARP probes from the button's MAC address as the button wakes up and attempts to join the Wi-Fi network.

However, initial research showed that Google's otherwise excellent 3rd-party developer integration is designed around speaking to Google Home, and for some good reasons (like not wanting apps sending you spoken ads) it's not possible to have the Home speak via an external app.

The Solution

I set up the buttons (except for picking a product to order), and then used an excellent Python server (see https://github.com/Nekmo/amazon-dash) to configure those buttons. It's a relatively simple install - you can use the example script, config file and service definition to allow it to run automatically on startup. I won't repeat those instructions here.

Now the Google Home part. Although there's no developer access to make Google Home speak, the Home is still a Cast device. So it's possible to send any audio to it via the Cast API and any client libraries thereof. If that audio is the output of Google's text-to-speech service, then it will sound exactly like Google Home itself (in my case the British female voice).

This is the approach taken by google-home-notifier - a NodeJS library, which comes with a basic example webapp. The setup is slightly tricky however.

1. First install NodeJS

2. Now install the latest github version of google-home-notifier:

https://github.com/noelportugal/google-home-notifier.git
cd google-home-notifier
npm install

3. You must modify the mdns dependency that was installed in the node_modules directory by applying this patch (works on mdns 2.3.3):

*** node_modules/mdns/lib/browser.js.bak 2017-10-01 10:01:29.131488019 +0100
--- node_modules/mdns/lib/browser.js 2017-10-01 10:04:23.556657163 +0100
***************
*** 115,123 ****
  }
  
  Browser.defaultResolverSequence = [
    rst.DNSServiceResolve()
! , 'DNSServiceGetAddrInfo' in dns_sd ? rst.DNSServiceGetAddrInfo() : rst.getaddrinfo()
  , rst.makeAddressesUnique()
  ];
  
  exports.browseThemAll = function browseThemAll(options) {
--- 115,124 ----
  }
  
  Browser.defaultResolverSequence = [
    rst.DNSServiceResolve()
! , 'DNSServiceGetAddrInfo' in dns_sd ? rst.DNSServiceGetAddrInfo() : rst.getaddrinfo({families:[4]})
! //, 'DNSServiceGetAddrInfo' in dns_sd ? rst.DNSServiceGetAddrInfo() : rst.getaddrinfo()
  , rst.makeAddressesUnique()
  ];
  
  exports.browseThemAll = function browseThemAll(options) {

4. Create a simple script to run the server:

#!/bin/bash

cd /path/to/google-home-notifier
node example.js

5. Copy it to /usr/bin

6. Using the service definition for the Python Dash button app as an example, create a service definition for the Notifier app:

[Unit]
Description=Google Home Text Bridge
[Service]
User=root
ExecStart=/usr/bin/google-home-notifier
Restart=always
RestartSec=3
 
[Install]
WantedBy=multi-user.target

7. Install it using the same process as the amazon-dash service

8. Install curl. We'll use it to call the google-home-notifier service, and you can use it for testing.

9. Now edit the amazon-dash /etc/amazon-dash.yml file to make the button call the google-home-notifier service:

settings:
  delay: 5
devices:
  aa:bb:cc:dd:ee:ff:
    name: Example
    user: joebloggs
    cmd: "curl -X POST -d \"text=Sit up straight\" http://localhost:8080/google-home-notifier"

10. Start both services, and you're done!

Limitations

There are some fairly big limitations:

  1. The dash buttons have to power on and connect to WiFi before it's possible to detect a button press. Remember they're designed for ordering items from Amazon, not tasks around the home. (see note 1)
  2. It's not possible using this implementation to have the button visually confirm (with a green light) successful execution of a command. (see note 2)
  3. There is a delay in turning the text into sound.
  4. There is a delay inherent in casting to Google Home
  5. Google Home makes the 'cast' tune before playing the audio

All in all, the series of delays means there is several seconds' latency between button press and final result.

Note 1: I have also considered whether it might be possible to reduce the delay when the button connects to WiFi by listening for the lower-level 802.11 traffic from the button, rather than waiting for it to connect and issue an ARP probe. This would require a wireless card of course, but in theory this is a slightly earlier event. However it's probably only milliseconds, so I doubt it is worth it.

Note 2: Confirmation on the button would be possible if the traffic between the button and Amazon server could be intercepted, but it would be quite complicated given this communication is via secure HTTPS and would have to be implemented at the router. The reason the ARP solution is easier is because those network packets are sent to all devices on the network, not just the router.

Comments

I am abit lost on apply the patch. anyone got the updated .js file or can paste the full text ?

What are you having trouble with exactly. If you have installed the same mdns version then you can just change that one line that is different, indicated by the exclamation marks. Or 'patch -p0 < patchfile.patch' will apply it. If the version you have is different it may already have been fixed, or require changing on a different line number.

Add new comment

(If you're a human, don't change the following field)
Your first name.
(If you're a human, don't change the following field)
Your first name.
(If you're a human, don't change the following field)
Your first name.

Normal input

  • Allowed HTML tags: <div> <span> <a> <em> <strong> <b> <i> <cite> <blockquote> <code> <pre> <ul> <ol> <li> <dl> <dt> <dd> <img> <div> <p> <br> <h1> <h2> <h3> <h4> <h5> <h6> <table> <tr> <td> <th> <thead> <tfoot> <hr>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.