├── .gitignore ├── package.json ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency directory 2 | node_modules 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "homebridge-amazondash", 3 | "version": "0.0.8", 4 | "description": "Amazon Dash plugin for homebridge: https://github.com/nfarina/homebridge", 5 | "license": "ISC", 6 | "keywords": [ 7 | "homebridge-plugin" 8 | ], 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/KhaosT/homebridge-amazondash.git" 12 | }, 13 | "bugs": { 14 | "url": "http://github.com/KhaosT/homebridge-amazondash/issues" 15 | }, 16 | "engines": { 17 | "node": ">=0.12.0", 18 | "homebridge": ">=0.3.3" 19 | }, 20 | "dependencies": { 21 | "node-dash-button": "^0.6.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # homebridge-amazondash 2 | 3 | Amazon Dash plugin for [Homebridge](https://github.com/nfarina/homebridge) 4 | 5 | ***As of iOS 10.2, Apple's Home app still don't support programable switch. Please use third party HomeKit app like Home or Hesperus to setup automation.*** 6 | 7 | ## Installation 8 | 9 | 1. Follow the [instruction](https://github.com/hortinstein/node-dash-button) to setup node-dash-button and figure out the MAC Address of the Dash Button. 10 | 2. Install this plugin using: npm install -g homebridge-amazondash 11 | 3. Update configuration file or use Homebridge's configuration service on iOS device to setup plugin. 12 | 4. Run Homebridge with elevated privileges. 13 | 14 | ### Config.json Example 15 | 16 | { 17 | "platform": "AmazonDash", 18 | "buttons": [ 19 | { 20 | "name": "Dash Blue", 21 | "mac": "74:c2:46:0a:f9:3f" 22 | }, 23 | { 24 | "name": "Dash Orange", 25 | "mac": "10:ae:60:4d:6a:0b" 26 | } 27 | ] 28 | } 29 | 30 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var dash_button = require('node-dash-button'); 2 | var Accessory, Service, Characteristic, UUIDGen; 3 | 4 | module.exports = function(homebridge) { 5 | Accessory = homebridge.platformAccessory; 6 | Service = homebridge.hap.Service; 7 | Characteristic = homebridge.hap.Characteristic; 8 | UUIDGen = homebridge.hap.uuid; 9 | 10 | homebridge.registerPlatform("homebridge-amazondash", "AmazonDash", DashPlatform, true); 11 | } 12 | 13 | function DashPlatform(log, config, api) { 14 | var self = this; 15 | 16 | self.log = log; 17 | self.config = config || { "platform": "AmazonDash" }; 18 | self.buttons = self.config.buttons || []; 19 | 20 | self.accessories = {}; // MAC -> Accessory 21 | 22 | if (api) { 23 | self.api = api; 24 | 25 | self.api.on('didFinishLaunching', self.didFinishLaunching.bind(this)); 26 | } 27 | } 28 | 29 | DashPlatform.prototype.configureAccessory = function(accessory) { 30 | var self = this; 31 | 32 | accessory.reachable = true; 33 | 34 | accessory 35 | .getService(Service.StatelessProgrammableSwitch) 36 | .getCharacteristic(Characteristic.ProgrammableSwitchEvent) 37 | .setProps({ 38 | maxValue: 0 39 | }); 40 | 41 | var accessoryMAC = accessory.context.mac; 42 | self.accessories[accessoryMAC] = accessory; 43 | } 44 | 45 | DashPlatform.prototype.didFinishLaunching = function() { 46 | var self = this; 47 | 48 | for (var i in self.buttons) { 49 | var button = self.buttons[i]; 50 | if (!self.accessories[button.mac]) { 51 | self.addAccessory(button.mac, button.name); 52 | } 53 | } 54 | 55 | var registedMACs = Object.keys(self.accessories); 56 | if (registedMACs.length > 0) { 57 | self.dash = dash_button(registedMACs, null, null, 'all'); 58 | self.dash.on('detected', function(dash_id) { 59 | var accessory = self.accessories[dash_id]; 60 | if (accessory) { 61 | self.dashEventWithAccessory(accessory); 62 | } 63 | }); 64 | } 65 | } 66 | 67 | DashPlatform.prototype.dashEventWithAccessory = function(accessory) { 68 | this.log.debug('Dash Event [%s]', accessory.displayName); 69 | var targetChar = accessory 70 | .getService(Service.StatelessProgrammableSwitch) 71 | .getCharacteristic(Characteristic.ProgrammableSwitchEvent); 72 | 73 | targetChar.setValue(0); 74 | } 75 | 76 | DashPlatform.prototype.addAccessory = function(mac, name) { 77 | var self = this; 78 | var uuid = UUIDGen.generate(mac); 79 | 80 | var newAccessory = new Accessory(name, uuid, 15); 81 | newAccessory.reachable = true; 82 | newAccessory.context.mac = mac; 83 | newAccessory.addService(Service.StatelessProgrammableSwitch, name); 84 | 85 | newAccessory 86 | .getService(Service.StatelessProgrammableSwitch) 87 | .getCharacteristic(Characteristic.ProgrammableSwitchEvent) 88 | .setProps({ 89 | maxValue: 0 90 | }); 91 | 92 | newAccessory 93 | .getService(Service.AccessoryInformation) 94 | .setCharacteristic(Characteristic.Manufacturer, "Amazon") 95 | .setCharacteristic(Characteristic.Model, "JK76PL") 96 | .setCharacteristic(Characteristic.SerialNumber, mac); 97 | 98 | this.accessories[mac] = newAccessory; 99 | this.api.registerPlatformAccessories("homebridge-amazondash", "AmazonDash", [newAccessory]); 100 | 101 | var dashButton = dash_button(mac, null, null, 'all'); 102 | dashButton.on('detected', function() { 103 | self.dashEventWithAccessory(newAccessory); 104 | }); 105 | } 106 | 107 | DashPlatform.prototype.removeAccessory = function(accessory) { 108 | if (accessory) { 109 | var mac = accessory.context.mac; 110 | this.api.unregisterPlatformAccessories("homebridge-amazondash", "AmazonDash", [accessory]); 111 | delete this.accessories[mac]; 112 | } 113 | } 114 | 115 | DashPlatform.prototype.configurationRequestHandler = function(context, request, callback) { 116 | if (request && request.type === "Terminate") { 117 | return; 118 | } 119 | 120 | if (!context.step) { 121 | var instructionResp = { 122 | "type": "Interface", 123 | "interface": "instruction", 124 | "title": "Before You Start...", 125 | "detail": "Please make sure homebridge is running with elevated privileges and you have setup the dependency follow the tutorial.", 126 | "showNextButton": true, 127 | "buttonText": "View Tutorial", 128 | "actionURL": "https://github.com/hortinstein/node-dash-button" 129 | } 130 | 131 | context.step = 1; 132 | callback(instructionResp); 133 | } else { 134 | switch (context.step) { 135 | case 1: 136 | var respDict = { 137 | "type": "Interface", 138 | "interface": "list", 139 | "title": "What do you want to do?", 140 | "items": [ 141 | "Add New Dash Button", 142 | "Disassociate Existed Dash Button" 143 | ] 144 | } 145 | context.step = 2; 146 | callback(respDict); 147 | break; 148 | case 2: 149 | var selection = request.response.selections[0]; 150 | if (selection === 0) { 151 | //Setup New 152 | var respDict = { 153 | "type": "Interface", 154 | "interface": "input", 155 | "title": "New Dash Button", 156 | "items": [ 157 | { 158 | "id": "name", 159 | "title": "Name", 160 | "placeholder": "Orange Dash" 161 | }, 162 | { 163 | "id": "mac", 164 | "title": "MAC Address (lowercase)", 165 | "placeholder": "11:22:33:44:aa:ff" 166 | } 167 | ] 168 | } 169 | context.step = 4; 170 | callback(respDict); 171 | } else { 172 | //Remove Exist 173 | var self = this; 174 | var buttons = Object.keys(this.accessories).map(function(k){return self.accessories[k]}); 175 | var names = buttons.map(function(k){return k.displayName}); 176 | 177 | var respDict = { 178 | "type": "Interface", 179 | "interface": "list", 180 | "title": "Which Dash Button do you want to remove?", 181 | "items": names 182 | } 183 | context.buttons = buttons; 184 | context.step = 6; 185 | callback(respDict); 186 | } 187 | break; 188 | case 4: 189 | var userInputs = request.response.inputs; 190 | var name = userInputs.name; 191 | var MACAddress = userInputs.mac; 192 | this.addAccessory(MACAddress, name); 193 | var respDict = { 194 | "type": "Interface", 195 | "interface": "instruction", 196 | "title": "Success", 197 | "detail": "The new dash button is now added.", 198 | "showNextButton": true 199 | } 200 | context.step = 5; 201 | callback(respDict); 202 | break; 203 | case 5: 204 | var self = this; 205 | delete context.step; 206 | var newConfig = this.config; 207 | var newButtons = Object.keys(this.accessories).map(function(k){ 208 | var accessory = self.accessories[k]; 209 | var button = { 210 | 'name': accessory.displayName, 211 | 'mac': accessory.context.mac 212 | }; 213 | return button; 214 | }); 215 | newConfig.buttons = newButtons; 216 | 217 | callback(null, "platform", true, newConfig); 218 | break; 219 | case 6: 220 | var selection = request.response.selections[0]; 221 | var accessory = context.buttons[selection]; 222 | this.removeAccessory(accessory); 223 | var respDict = { 224 | "type": "Interface", 225 | "interface": "instruction", 226 | "title": "Success", 227 | "detail": "The dash button is now removed.", 228 | "showNextButton": true 229 | } 230 | context.step = 5; 231 | callback(respDict); 232 | break; 233 | } 234 | } 235 | } 236 | --------------------------------------------------------------------------------