Monday, 29 May 2017

Tutorial - using Amazon Dash with a Pi Zero and node.js

Introduction

In MagPi Magazine (issue 57) there was an article about how you can subvert an Amazon Dash button and use it as a Internet of Things ("IoT") button. However, the code in the article had several syntax errors. Once I fixed the syntax errors I still couldn't get it to work with my button. Then I found that a key ingredient, tcpdump (needed by scapy), was missing - so I installed that too. I ended up with some working code but it wasn't very reliable, especially using wireless on the Raspberry Pi Zero W. After doing a bit of research and experimentation, I got it working better by using "dasher", a node.js script.

Node.js is a server-based runtime environment for executing JavaScript code. It allows non-blocking multi-threading applications to be deployed. This is a fancy way of saying that you can leave it running in the background on your computer (Raspberry Pi in this case) and it will just sit there listening until something triggers it (without consuming lots of processing power).

A companion application, NPM, is a package manager for node.js scripts.

As in the MagPi article, dasher would allow the Dash button to toggle an IoT bulb such as the Lifx or Philips Hue. I don't have one of those so I'll use a simple LED and then move on to IFTTT. Rather than tie up a Raspberry Pi 3, I thought this would be a good use for the cheap-as-chips Raspberry Pi Zero W. Incidentally, although this works well on a Pi Zero W, it doesn't seem to be very reliable using a basic Pi Zero with a wireless dongle attached to the USB port. If you've only got a non-wireless Pi Zero, it does work well with a wired network connection (e.g. a USB hub with a network port) if you happen to have one lying around.

Before we install dasher, the Dash button needs to be configured following the Amazon instructions so that it stores the SSID and password of your home wireless network (but with the last step omitted - so that no product is associated with the button). Strangely, you need to use an Android or iOS mobile phone to do this - not a tablet. If, like me, you've only got a tablet, you can use a little-known Android app called Amazon Underground on an Android tablet. I forgot to say - if you order something with the button of your choice, you get the price of the button refunded, so it makes sense to get a button for something you use anyway, place one order then de-activate the button. Then go through the "add button" process again and quit the app before the final step.

How will we know if we have got our Dash button working? It makes sense to test it by controlling a GPIO pin first.

Let's get started

I started by following the instructions on the dasher git-hub page. However, my initial attempt didn't work; also, the version of node.js that you end up with is v4.0, although the current  (at the time of writing) version is 6.10.3. I thought I'd do it properly and install the latest version of node.js. First, I prepared my Raspberry Pi Zero W with a new image of Raspbian (date 10 April 2017). The current version of Raspbian comes with a very old version of  node, and this has to be updated for dasher to work properly. After configuring the timezone and WiFi interface, I installed a couple of pre-requisites (libpcap-dev and npm).
  • Open console and install node, libpcap-dev and npm
    #Install node.js to remove nodejs legacy 
    sudo apt-get install node
    sudo apt-get install libpcap-dev
    sudo apt-get install npm

    • Install newer version of Node.JS (I used ARM v6)
    wget https://nodejs.org/dist/v6.10.3/node-v6.10.3-linux-armv6l.tar.gz
    tar -xvf node-v6.10.3-linux-armv6l.tar.gz
    cd  node-v6.10.3-linux-armv6l
    sudo cp -R * /usr/local/
    sudo node --version


    This should respond with v6.10.3. If so, node is installed and up to date so we can proceed with installing dasher.
    • Install dasher
    cd ..
    sudo git clone https://github.com/maddox/dasher.git
    cd dasher
    sudo npm install

    N.B. ignore gyp warnings (doesn't seem to matter)
    Go and make a cup of coffee - installation takes a long time

    When that is finished, check that your button can be found (make a note of the MAC address if you haven't already done so)....
    sudo ./script/find_button

    Toggle a GPIO pin using dasher

    We can configure dasher to monitor the home network and run a shell script if it sees a "UDP" request from the MAC address of the Dash button to join the network (which it will do when the button is pressed). To do this, we need to modify the config.json file according to the example given in the dasher documentation. Mine looks like this:

    {"buttons":[
        {
           "name": "Command Exec Button",
           "address": "[MY MAC ADDRESS]",
           "cmd": "/home/pi/dash_button.sh"
        }
    ]}

    Now we need to write the shell script (dash_button.sh) to toggle a GPIO. I connected an LED (with resistor) to GPIO14 so that, when the Dash button is pressed, it toggles on/off.

    #!/bin/bash
    GPIO=14
    GPIODIR=/sys/class/gpio/gpio$GPIO

    # i.e. GPIODIR = /sys/class/gpio/gpio14
    # use $GPIODIR to save typing long folder trees
    echo "Configuring GPIO $GPIO"

    #check if gpio14 is already exported.
    # If not, export gpio14 then set as output
    if [ ! -e "$GPIODIR" ] #tests for the (not !) existence of gpio14 folder
    then
            echo "Exporting GPIO"
            echo $GPIO > /sys/class/gpio/export
    echo out > $GPIODIR/direction #set as output
    else
            echo "GPIO already exported"
    fi

    #now that gpio14 is configured, toggle on/off depending on state
    read state < $GPIODIR/value
    sleep 1
    echo $(( ! state )) > $GPIODIR/value


    Before you can run this shell script, you need to make it executable and add it to your path:
    sudo chmod -u+x dash_button.sh
    sudo cp dash_button.sh /usr/local/bin


    To run dasher:
    sudo npm run start

    IFTTT

    Now that we have got the button working with GPIO pins, we can use it to set up a trigger action using IFTTT instead (you can't do both with the same button, unfortunately). IFTTT allows you to connect web-enabled "services" together so that, IF something happens on one service (a "THIS" trigger), THEN an action will occur on another service (a "THAT" action).

    Assuming you have a Gmail account, a good one for testing purposes is to add a row to a Google Sheet each time the button is pressed.

    In this case, the "dasher" script will monitor the home network and trigger an HTTP request if it sees a "UDP" request from the MAC address of the Dash button to join the network (which it will do when the button is pressed).

    The next step is to configure the IFTTT "Maker Webhooks" service to "watch" for the HTTP request that the dasher script will generate when it sees the Dash button join the network. Assuming you have set up an account on IFTTT, you will need to activate the Maker Webhooks service which will generate your own private key (you will need this later).

    Now, in IFTTT you can build your applet. Click on the "+" in the applet builder and choose the Maker Webhooks service (Receive a web request) as the trigger. Give it a name (in my case "trigger_dash"). Now click on the "+" to add an action. Choose the "Google Drive" service, then "Add row to spreadsheet". You can leave all the defaults as they are and just press "Create action", then (on the next page that appears) "Finish". If you click on  the down arrow below your IFTTT user name on the main IFTTT page (top right) and choose Services, then choose the Maker Webhooks service you will be able to click on the Documentation button for that service (top right again) you will see the full URL that you need to generate (in dasher) in order to trigger the applet. This takes the format

    https://maker.ifttt.com/trigger/[EVENT NAME]/with/key/[YOUR KEY]

    Next make your own config.json file containing the mac address of your button and Maker Webhooks key. I recommend that you first rename or copy the config.json file you made to toggle a GPIO pin to config2.json or something else, so that you can always revert back to it if you need to test things are working.

    sudo nano /config/config.json

    Mine looks like this (note that "trigger_dash" is the name I used  in Maker Webhooks when setting up the IFTTT applet):
    {"buttons":[
       {
            "name": "trigger_dash",
            "protocol": "udp",
            "address": "[YOUR MAC ADDRESS]",
            "url": "https://maker.ifttt.com/trigger/trigger_dash/with/key/[YOUR KEY]",
            "method": "POST"
        }
    ]}


    I have kept it simple for testing purposes. Once you get it working you can add more buttons, or pass more information to IFTTT.
    sudo npm run start

    With any luck, you should see dasher start running and confirm that the button has been added. Now, we can configure it so that it runs automatically, every time that you start up the Raspberry Pi.

    Run from startup

    Just follow the instructions here. These instructions also show you how to check that your Dash button is being detected. Now, you should be able to reboot your Raspberry Pi then, every time the Dash button is pressed, Google will add a new line to a spreadsheet. If it doesn't work, open a console and type:
    tail -f /var/log/dasher.log

    That should show you if dasher detected the button press, and what it did. I have found that the IFTTT Google service doesn't always work. Good luck!

    7 comments:

    1. Great tutorial. I had been struggling with others which didn't have the most recent IFTTT changes. Also the GPIO part is very useful as i want to use a Dasher button with the RPi Google AIY project.

      ReplyDelete
      Replies
      1. I have successfully configured Dash buttons to reboot and shut down the RPi using Dasher. My RPi is connected to a WeMo switch/socket which I should be able to toggle on and off using yet another Dash button (this will start my RPi from being shutdown) through IFTTT but the existing tutorials don't reflect the recent changes to IFTTT. any ideas on what I need to do to achieve this ?

        Delete
    2. Thanks Insepes!

      KevinRPi. If you follow my IFTTT example but, instead of choosing the "Google/Add Row to Spreadsheet" as the Action service, add the "WeMo Smart Plug" (toggle on/off) instead, that should work.

      ReplyDelete
    3. Hi great tutorial for some reason im able to get the mac address using the sudo ./script/find_button over wifi connection with my raspberry zero w however its not being detected in the running script (sudo npm run start) and due to this fact noting is being triggered.

      note I've tried to connected over the LAN and its working normally, appreciate your help.

      thanks

      ReplyDelete
    4. Sorry for the slow reply but I'm away on holiday so I can't test anything. If it works over LAN then it's set up properly. WiFi can be a bit hit and miss at times but sounds like yours is not working at all. Can you try it right next to your WiFi router?

      ReplyDelete
    5. Would this still work over a mesh network (google WiFi). I wonder if the button will get picked up if they are connected on different levels.

      ReplyDelete
    6. Hi Max. I think it should work better if anything (but I haven't tried it).

      ReplyDelete