├── .gitignore ├── README.md ├── _config.yml ├── bin └── mqtt-dasher ├── mqtt-dasher.service ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | *.log 36 | config.yml 37 | 38 | artifacts 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MQTT Dasher 2 | ***Emits events to MQTT when an Amazon Dash button is pressed.*** 3 | 4 | [![GitHub tag](https://img.shields.io/github/tag/stjohnjohnson/mqtt-dasher.svg)](https://github.com/stjohnjohnson/mqtt-dasher/releases) 5 | 6 | # Configuration 7 | 8 | The dash bridge has one yaml file for configuration: 9 | 10 | ``` 11 | --- 12 | mqtt: 13 | # Specify your MQTT Broker's hostname or IP address here 14 | host: mqtt 15 | # Preface for the topics $PREFACE/$TOPIC 16 | preface: dash 17 | 18 | buttons: 19 | 44:65:0d:dc:51:50: nerf_supplies 20 | 21 | ``` 22 | 23 | # Usage 24 | 25 | _note: follow [this setup](https://github.com/hortinstein/node-dash-button#installation-instructions) first 26 | 27 | 1. Install the Node module globally 28 | 29 | ``` 30 | $ npm install -g mqtt-dasher 31 | ``` 32 | 33 | 2. Configure your buttons 34 | 35 | ``` 36 | $ mkdir -p /opt/mqtt-dasher 37 | $ cp _config.yml /opt/mqtt-dasher/config.yml 38 | $ vi /opt/mqtt-dasher/config.yml 39 | ``` 40 | 41 | 3. Add systemd unit for the service 42 | 43 | ``` 44 | $ cp mqtt-dasher.service /etc/systemd/system 45 | ``` 46 | 47 | 4. Run the server 48 | 49 | ``` 50 | $ systemctl start mqtt-dasher.service 51 | ``` 52 | 53 | 5. Configured dash events now feed into MQTT 54 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | mqtt: 3 | # Specify your MQTT Broker's hostname or IP address here 4 | host: mqtt 5 | port: 1883 6 | #leave empty if none 7 | username: 8 | password: 9 | # Preface for the topics $PREFACE/$TOPIC 10 | preface: dash 11 | 12 | buttons: 13 | 44:65:0d:dc:51:50: dash/nerf_supplies 14 | -------------------------------------------------------------------------------- /bin/mqtt-dasher: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../server.js'); 4 | -------------------------------------------------------------------------------- /mqtt-dasher.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=MQTT Dasher 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | Environment=CONFIG_DIR=/opt/mqtt-dasher 8 | ExecStart=/usr/bin/mqtt-dasher 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mqtt-dasher", 3 | "version": "0.1.0", 4 | "description": "Notifies MQTT when an Amazon Dash button is pressed", 5 | "main": "server.js", 6 | "bin": { 7 | "mqtt-dasher": "./bin/mqtt-dasher" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/stjohnjohnson/mqtt-dasher.git" 12 | }, 13 | "keywords": [ 14 | "mqtt", 15 | "dash", 16 | "amazon", 17 | "arp" 18 | ], 19 | "author": "St. John Johnson ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/stjohnjohnson/mqtt-dasher/issues" 23 | }, 24 | "homepage": "https://github.com/stjohnjohnson/mqtt-dasher", 25 | "dependencies": { 26 | "async": "^2.0.0", 27 | "js-yaml": "^3.6.1", 28 | "mqtt": "^1.12.0", 29 | "node-dash-button": "^0.5.0", 30 | "request": "^2.73.0", 31 | "winston": "^2.2.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /*jslint node: true */ 2 | 'use strict'; 3 | 4 | var winston = require('winston'), 5 | path = require('path'), 6 | fs = require('fs'), 7 | yaml = require('js-yaml'), 8 | async = require('async'), 9 | mqtt = require('mqtt'), 10 | fs = require('fs'), 11 | DashButton = require('node-dash-button'); 12 | 13 | var CONFIG_DIR = process.env.CONFIG_DIR || process.cwd(), 14 | CONFIG_FILE = path.join(CONFIG_DIR, 'config.yml'), 15 | SAMPLE_FILE = path.join(__dirname, '_config.yml'), 16 | CURRENT_VERSION = require('./package').version; 17 | 18 | var config, 19 | buttons = {}, 20 | broker, 21 | timeouts = {}; 22 | 23 | // Show Debug logs in console 24 | winston.level = 'debug'; 25 | 26 | /** 27 | * Load user configuration (or create it) 28 | * @method loadConfiguration 29 | * @return {Object} Configuration 30 | */ 31 | function loadConfiguration () { 32 | if (!fs.existsSync(CONFIG_FILE)) { 33 | fs.writeFileSync(CONFIG_FILE, fs.readFileSync(SAMPLE_FILE)); 34 | } 35 | 36 | return yaml.safeLoad(fs.readFileSync(CONFIG_FILE)); 37 | } 38 | 39 | /** 40 | * Notify the broker that something triggered 41 | * @method notifyMQTT 42 | * @param {String} topic Topic to message 43 | * @param {Boolean} value Value to set (ON, OFF) 44 | */ 45 | function notifyMQTT (topic, value) { 46 | var state = value ? 'active': 'inactive'; 47 | 48 | winston.debug('Notifying MQTT %s with %s', topic, state); 49 | broker.publish(topic, state, { 50 | retain: true 51 | }, function (err) { 52 | if (err) { 53 | winston.error('Error notifying MQTT', err); 54 | } 55 | }); 56 | } 57 | 58 | /** 59 | * Handle an events from the Amazon Dash 60 | * @method buttonEvent 61 | * @param {String} mac Mac Address (identifier) 62 | * @param {String} topic MQTT topic to write to 63 | */ 64 | function buttonEvent (mac, topic) { 65 | winston.info('Button press detected on %s for %s', mac, topic); 66 | 67 | // Auto-clear action after 10 seconds 68 | clearTimeout(timeouts[topic]); 69 | timeouts[topic] = setTimeout(notifyMQTT.bind(null, topic, false), 10000); 70 | 71 | // Notify MQTT 72 | notifyMQTT(topic, true); 73 | } 74 | 75 | // Main flow 76 | async.series([ 77 | function loadFromDisk (next) { 78 | winston.info('Starting MQTT Amazon Dash - v%s', CURRENT_VERSION); 79 | winston.info('Loading configuration'); 80 | config = loadConfiguration(); 81 | 82 | process.nextTick(next); 83 | }, 84 | function connectToMQTT (next) { 85 | winston.info('Connecting to MQTT at mqtt://%s', config.mqtt.host); 86 | var mqtt_broker_options = config.mqtt; 87 | broker = mqtt.connect(mqtt_broker_options); 88 | broker.on('connect', function () { 89 | next(); 90 | // @TODO Not call this twice if we get disconnected 91 | next = function () {}; 92 | }); 93 | }, 94 | function setupButtons (next) { 95 | winston.info('Listening for %d buttons', Object.keys(config.buttons).length); 96 | 97 | Object.keys(config.buttons).forEach(function (macAddress) { 98 | var topic = config.buttons[macAddress]; 99 | if (config.mqtt.preface) { 100 | topic = config.mqtt.preface + '/' + topic; 101 | } 102 | buttons[macAddress] = DashButton(macAddress); 103 | buttons[macAddress].on('detected', buttonEvent.bind(null, macAddress, topic)); 104 | }); 105 | 106 | process.nextTick(next); 107 | } 108 | ], function (error) { 109 | if (error) { 110 | return winston.error(error); 111 | } 112 | winston.info('Waiting for dash buttons to be pressed'); 113 | }); 114 | --------------------------------------------------------------------------------