├── .gitignore ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── dashed.gemspec ├── exe └── find_dash └── lib ├── dashed.rb └── dashed ├── button.rb ├── packet.rb └── version.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in dashed.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Justin Kenyon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dashed 2 | 3 | Dashed is a Ruby library for interacting with Amazon Dash button presses. 4 | 5 | ## Installation 6 | 7 | Just add `dashed` to your Gemfile and then run `bundle install`. 8 | 9 | Dashed has to be run with root permissions in order to listen to your network 10 | devices to pick up your dash button. 11 | 12 | ## Preparing the buttons 13 | 14 | In order to use your [dash buttons](http://www.amazon.com/oc/dash-button) with this library, you will need to set them up using Amazon's setup process. While you can automatically order products using the buttons, if you'd like to prevent the automatic ordering of a product, simply stop the registration process before choosing a product. 15 | 16 | It is recommended that you read [this post](https://medium.com/@edwardbenson/how-i-hacked-amazon-s-5-wifi-button-to-track-baby-data-794214b0bdd8) on Medium by Edward Benson describing how he uses the buttons. The article walks you through the process of getting the buttons set up; once you have reached the point that the button is connected through WiFi, this gem will take you the rest of the way! 17 | 18 | 19 | ## Usage 20 | 21 | Dashed includes the `find_dash` executable which looks for Amazon Dash buttons 22 | based on their mac address. Once you've found your Dash buttons Mac Address you 23 | can get started. 24 | 25 | ex: `sudo find_dash dummylan0` where dummylan0 is the wifi interface to listen 26 | on. Ensure your Amazon Dash button is connected to the same network. 27 | 28 | Here is an example implementation: 29 | 30 | ``` 31 | Dashed::Button.new("ff:ff:ff:ff:ff", "dummylan0").on_press do 32 | puts "I was pressed!" 33 | end 34 | ``` 35 | 36 | Please be aware that `on_press` blocks the execution of the current 37 | thread. If you don't want it to block please create the button in its own 38 | thread. 39 | 40 | ## Known Issues 41 | 42 | - Buttons can not be sniffed out on some OS X machines 43 | - Some buttons seem to have a different version of the Amazon Dash button 44 | software that render them useless for this. 45 | 46 | ## Contributing 47 | 48 | Bug reports and pull requests are welcome on GitHub at 49 | https://github.com/kenyonj/dashed. 50 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /dashed.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'dashed/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "dashed" 8 | spec.version = Dashed::VERSION 9 | spec.authors = ["Justin Kenyon", "Blake Williams"] 10 | spec.email = ["kenyonj@gmail.com", "blake@blakewilliams.me"] 11 | 12 | spec.summary = %q{Library for interacting with Amazon Dash buttons} 13 | spec.description = %q{Library for interacting with Amazon Dash buttons} 14 | spec.homepage = "https://github.com/kenyonj/dashed" 15 | spec.license = "MIT" 16 | 17 | # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or 18 | # delete this section to allow pushing this gem to any host. 19 | if spec.respond_to?(:metadata) 20 | spec.metadata['allowed_push_host'] = "https://rubygems.org" 21 | else 22 | raise "RubyGems 2.0 or newer is required to protect against public gem pushes." 23 | end 24 | 25 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 26 | spec.bindir = "exe" 27 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 28 | spec.require_paths = ["lib"] 29 | 30 | spec.add_development_dependency "bundler", "~> 1.10" 31 | spec.add_development_dependency "rake", "~> 10.0" 32 | spec.add_dependency "pcaprub", "~> 0.12.0" 33 | end 34 | -------------------------------------------------------------------------------- /exe/find_dash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'pcaprub' 4 | require_relative '../lib/dashed/packet' 5 | 6 | interface = ARGV[0] 7 | 8 | if interface 9 | capture = PCAPRUB::Pcap.open_live(interface, 65535, true, 0) 10 | capture.setfilter("arp") 11 | 12 | capture.each_packet do |raw_packet| 13 | packet = Dashed::Packet.new(raw_packet) 14 | if packet.host_mac_address.start_with? "74:75" 15 | puts "Potential Amazon Dash Button #{packet.host_mac_address}" 16 | end 17 | end 18 | else 19 | puts "You must provide an interface" 20 | end 21 | -------------------------------------------------------------------------------- /lib/dashed.rb: -------------------------------------------------------------------------------- 1 | require "dashed/version" 2 | require "dashed/button" 3 | 4 | module Dashed 5 | end 6 | -------------------------------------------------------------------------------- /lib/dashed/button.rb: -------------------------------------------------------------------------------- 1 | require "pcaprub" 2 | require "time" 3 | require "dashed/packet" 4 | 5 | module Dashed 6 | class Button 7 | TIMEOUT = 45 8 | SNAPLENGTH = 65535 9 | 10 | attr_accessor :timeout 11 | attr_reader :last_press, :mac_address 12 | 13 | def initialize(mac_address, interface, timeout = TIMEOUT) 14 | @mac_address = mac_address 15 | @interface = interface 16 | @timeout = timeout 17 | @last_press = nil 18 | end 19 | 20 | def on_press 21 | capture.each_packet do |raw_packet| 22 | packet = Packet.new(raw_packet) 23 | if packet.host_mac_address == mac_address && !duplicate_arp? 24 | @last_press = Time.now 25 | yield if block_given? 26 | end 27 | end 28 | end 29 | 30 | private 31 | 32 | def capture 33 | return @capture if @capture 34 | 35 | @capture = PCAPRUB::Pcap.open_live(@interface, SNAPLENGTH, true, 0) 36 | @capture.setfilter("arp") 37 | end 38 | 39 | def duplicate_arp? 40 | last_press && (last_press - Time.now).abs < 45 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/dashed/packet.rb: -------------------------------------------------------------------------------- 1 | module Dashed 2 | class Packet 3 | attr_reader :raw_packet 4 | 5 | def initialize(raw_packet) 6 | @raw_packet = raw_packet 7 | end 8 | 9 | def host_mac_address 10 | raw_packet. 11 | data. 12 | unpack("C*"). 13 | map { |i| i.to_s(16) }. 14 | map { |i| if i.length == 1 then "0#{i}" else i end}. 15 | slice(6, 6). 16 | join(":") 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/dashed/version.rb: -------------------------------------------------------------------------------- 1 | module Dashed 2 | VERSION = "0.3.0" 3 | end 4 | --------------------------------------------------------------------------------