Last active
April 22, 2020 17:59
-
-
Save rduplain/9e73198a25e30bd83bb1ea315c27fddf to your computer and use it in GitHub Desktop.
Amazon Dash→LIFX Power
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Detect Amazon Dash button network broadcast, toggle LIFX power based on MAC. | |
# | |
# Usage, with Amazon Dash buttons connected to LAN WiFi (see below for setup): | |
# | |
# 0. Configure `BUTTONS` with MAC to LIFX selector & `LIFX` with token. | |
# 1. Configure network firewall to block WAN out from dash MAC. | |
# 2. Install dependencies with `pip install requests scapy` on Python 3. | |
# 3. Run as root. | |
# | |
# Alternatively, to run Scapy without root: | |
# | |
# setcap cap_net_raw=eip /path/to/python | |
# setcap cap_net_raw=eip `which tcpdump` | |
# | |
# Note that this `setcap` usage opens up raw socket access to all users. | |
# | |
# This packet-sniffing program can run anywhere on the LAN from a non-WiFi | |
# hard-wired host. On button press, the dash button connects to WiFi and issues | |
# a DHCP request in order to get its IP and DNS settings. This DHCP request | |
# (with related ARP packets) is broadcast on the network. The following program | |
# detects the DHCP request by sniffing LAN traffic and dispatches the connected | |
# Python function. | |
# | |
# A simple debounce method ensures that only one dispatch is made within a few | |
# seconds, to avoid dispatch on multiple DHCP packets for the same button | |
# press. The connection is passive in that no response is sent to the dash | |
# button. The observed behavior of the dash button is a blinking white LED that | |
# eventually times out. While the white LED is lit, additional button presses | |
# do nothing, as no further DHCP request is made. The button then powers down | |
# about half a minute later, and a button press during an unlit LED then starts | |
# the process again, issuing a new DHCP request, to be picked up by this | |
# packet-sniffing program. | |
# | |
# Meaning, after pressing the dash button, wait until the white LED is unlit | |
# before attempting to press the button again. | |
# | |
# Altogether, this process is slow and takes several seconds. | |
# | |
# To get an Amazon Dash button onto local WiFI, noting that a button may need | |
# an attempted setting of amzn_ssid via `curl` (which fails) before it accepts | |
# the .wav payload: | |
# | |
# blog.christophermullins.com/2019/12/20/rescue-your-amazon-dash-buttons/ | |
# | |
# LIFX personal access token: | |
# | |
# cloud.lifx.com/settings | |
import datetime | |
import requests | |
from scapy.all import Ether, sniff | |
LIFX = '0123456789abcdef01234567890abcdef01234567890abcdef01234567890abc' | |
BUTTONS = { | |
'01:23:45:67:89:AB': 'd0123456789a', | |
'12:34:56:78:9A:BC': 'group_id:0123456789abcdef0123456789abcdef', | |
} | |
LAST_SEEN = {} | |
DEBOUNCE_SECONDS = 25 | |
def toggle(selector): | |
requests.post( | |
f'https://api.lifx.com/v1/lights/{selector}/toggle', | |
headers={'Authorization': f'Bearer {LIFX}'}) | |
def detect_button(pkt): | |
MAC = pkt[Ether].src.upper() | |
if MAC in BUTTONS: | |
now = datetime.datetime.now() | |
timestamp = int(now.timestamp()) | |
if timestamp > LAST_SEEN.get(MAC, 0) + DEBOUNCE_SECONDS: | |
LAST_SEEN[MAC] = timestamp | |
print(now.strftime(f'%Y-%m-%d %H:%M:%S - {MAC}')) | |
toggle(BUTTONS[MAC]) | |
def main(): | |
# Performance Note: | |
# | |
# Filter on ARP, not DHCP, as Scapy provides a simple means to specify | |
# `filter='arp'`. Otherwise, filtering on DHCP requires filter syntax with | |
# UDP packets and additional processing to detect DHCP layer. | |
sniff(prn=detect_button, filter='arp', store=0) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Tested with: