The other day, "Amazon Dash Button" has finally been released in Japan. The point is that when you click the button, the target product is automatically ordered by Amazon and delivered to your home. Let's make this ourselves this time! It is an article to that effect.
Those who understand it will understand, but to be exact, those who say "AWS IoT Button" which is a programmable version of "Amazon Dash Button" I think it's close.
The overall architecture is as follows:
I will explain in detail from the back of the process (in the order of 3-> 2-> 1).
In this chapter, we will use Selenium and PhantomJS to crawl Amazon's website and create a Lambda function that automatically purchases the specified item.
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/welcome.html When AWS Lambda uploads your code to AWS Lambda, the service becomes a computing service that uses your AWS infrastructure to take over the execution of your code.
Java, Node.js, Python, C # can be selected for the runtime, but this time we will use Python.
Place the Lambda function body and necessary libraries directly under the project directory.
$ tree -L 1
.
├── amzorderer.py #Lambda function body
├── phantomjs #PhantomJS binary
├── selenium #Selenium library for Python
└── selenium-3.0.2.dist-info
Reference: https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html
Install Selenium directly under the project directory using pip.
pip install selenium -t /path/to/project-dir
Download the Linux 64bit version tar from the Official of PhantomJS, and place phantomjs under the bin directory directly under the project directory.
The source code is also available below. (Scheduled to be released later)
amzorderer.py
# -*- coding:utf-8 -*-
__author__ = 'H.Takeda'
__version__ = '1.0.0'
import os
import boto3
from argparse import ArgumentParser
from base64 import b64decode
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
# Amazon top page url.
AMAZON_URL = "https://www.amazon.co.jp"
# Amazon user id (email).
AMAZON_USER = boto3.client('kms').decrypt(
CiphertextBlob=b64decode(os.environ['user']))['Plaintext']
# Amazon user password.
AMAZON_PASS = boto3.client('kms').decrypt(
CiphertextBlob=b64decode(os.environ['password']))['Plaintext']
# User agent.
USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/53 (KHTML, like Gecko) Chrome/15.0.87"
# Item dictionary.
ITEMS = {
"01": "1fLhF7q", # Toilet Paper
"02": "fhYcbp7" # Saran Wrap
}
def lambda_handler(event, context):
# Create web driver for PhantomJS.
dcap = dict(DesiredCapabilities.PHANTOMJS)
dcap["phantomjs.page.settings.userAgent"] = USER_AGENT
driver = webdriver.PhantomJS(desired_capabilities=dcap,
service_log_path=os.path.devnull,
executable_path="/var/task/phantomjs")
# Get amazon top page.
driver.get(AMAZON_URL)
# Transition to sign in page.
driver.find_element_by_id("nav-link-yourAccount").click()
# Input user id.
driver.find_element_by_id("ap_email").send_keys(AMAZON_USER)
# Input user password.
driver.find_element_by_id("ap_password").send_keys(AMAZON_PASS)
# Sign in.
driver.find_element_by_id("signInSubmit").click()
# Select item.
driver.get("http://amzn.asia/" + ITEMS[event["item"]])
# Add to cart.
driver.find_element_by_id("add-to-cart-button").click()
# Proceed to checkout.
driver.find_element_by_id("hlb-ptc-btn-native").click()
# Order.
# driver.find_element_by_name("placeYourOrder1")[0].click()
driver.save_screenshot("hoge.png ")
driver.quit()
Basically, I'm just doing a simple Selenium operation, but I will pick up the points.
ghostdriver.log
) is created in the current directory by default, do not output (/ dev / null).Zip the project directory. The name of the zip file does not matter.
$ zip -r upload.zip /path/to/project-dir/*
Set the Lambda function from the AWS management console. You can also upload the created zip file here.
Set the test event (parameter to be passed to the Lambda function) from Actions> Configure test event, Press the Test button to execute the Lambda function.
It is OK if the process is completed normally. This completes the creation of the Lambda function that automatically purchases on Amazon.
In this chapter, AWS IoT is used to accept MQTT requests and configure settings to call the Lambda function created above.
https://aws.amazon.com/jp/iot/how-it-works/ By using AWS IoT, it is possible to connect various devices to various AWS services, protect data and communication, and perform processing and actions on device data.
Register the device that connects to AWS IoT from the AWS IoT console screen.
2. Select the environment information for the client device. This time, we will connect to AWS IoT from Raspberry Pi Zero (OS: Raspbian) using the Python SDK.
3. Enter an arbitrary device name (raspi0 in this case) and press "Next step".
4. Press "Liux / OS X" to download the public key, private key, and client certificate (you will use them later). Click "Next step" to proceed to the next screen.
5. The device setting procedure will appear, so press "Done" to complete.
Set your Lambda function to be called when a message is published to a particular topic.
2. Enter an arbitrary rule name (amzorderer in this case) to set the rule query. When the message is published to the "amzordere" topic, extract the value of the item attribute and execute the Action.
3. Select Call Lambda function as Action.
4. The function to call is the "amzorderer" created earlier.
5. Click "Create rule" to complete the creation.
In this chapter, we will use Raspberry Pi to create a client device with buttons.
Needless to say, it will be a small model of the well-known Raspberry Pi. It has an exceptional price of about $ 5 with a CPU clock of 1GHz and memory of 512MB. This time, using this Raspberry Pi Zero, I would like to create a device that sends a request to AWS IoT at the push of a button.
We will make it using basic electronic work parts that are found in every home.
Since there is only one miniUSB terminal, it is convenient to have a USB hub at the time of initial setting.
Install Raspbian (Jessie) by referring to Around here.
This time, I will connect the Raspberry Pi Zero to the wireless LAN using Buffalo's wireless slave unit.
Connect the wireless LAN slave unit to USB.
If you type the lsusb
command, you can see that it recognizes the slave unit.
$ lsusb
Bus 001 Device 002: ID 0411:01a2 BUFFALO INC. (formerly MelCo., Inc.) WLI-UC-GNM Wireless LAN Adapter [Ralink RT8070]
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Use the wpa_passphrase
command to generate the SSID and password required for wireless LAN connection.
$ wpa_passphrase [SSID] [Passphrase]
network={
ssid=[SSID]
#psk=[Passphrase] <-You can delete this line
psk=[Encrypted passphrase]
}
Copy the above text and add it to /etc/wpa_supplicant/wpa_supplicant.conf
.
/etc/wpa_supplicant/wpa_supplicant.conf
country=GB
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
ssid=[SSID]
psk=[Encrypted passphrase]
}
Edit /etc/dhcpcd.conf
and make it a fixed IP.
Set the IP address, router, and DNS according to your environment.
interface wlan0
static ip_address=192.168.11.30/24
static routers=192.168.11.1
static domain_name_servers=192.168.11.1
If you can restart and SSH connection from the mother ship, the setting is completed.
$ sudo shutdown -r now
Unlike other models, soldering is required when using GPIO with Raspberry Pi Zero. It will be a finer work than I expected, so it is better to use a solder alignment of φ0.6 mm.
Honestly, electronic work is an amateur, but I will build it with reference to the information I searched on the net. The GPIO layout of Raspberry Pi Zero is as follows.
The power supply of 3.3V is taken from No. 1, and ON / OFF of GPIO25 tact switch is received by GPIO9. Insert a 10k ohm resistor between GPIO25 and GND. This is called a pull-down resistor and plays a role in reliably transmitting a HIGH (3.3V) or LOW (0V) signal. I'm sorry that the image is difficult to understand.
Now that the circuit is assembled, we will create a program that receives the input of the tact switch and publishes the message to AWS IoT.
Python 2.7 is installed on Raspbian (Jessie), so I will write the program in Python.
$ python -V
Python 2.7.9
The Python SDK for connecting to AWS IoT is open to the public, so use it.
$ sudo pip install AWSIoTPythonSDK
publisher.py
# -*- coding:utf-8 -*-
__author__ = 'H.Takeda'
__version__ = '1.0.0'
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
from argparse import ArgumentParser
import json
import logging
import RPi.GPIO as GPIO
import signal
import sys
import time
def configure_logging():
# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)
def parse():
argparser = ArgumentParser()
argparser.add_argument("-e", "--endpoint", type=str, required=True)
argparser.add_argument("-r", "--rootCA", type=str, required=True)
argparser.add_argument("-c", "--cert", type=str, required=True)
argparser.add_argument("-k", "--key", type=str, required=True)
args = argparser.parse_args()
return vars(args)
def careate_client(endpoint, root_ca, cert, private_pey):
# For certificate based connection.
client = AWSIoTMQTTClient("raspi0")
# Configurations
client.configureEndpoint(endpoint, 8883)
client.configureCredentials(root_ca, private_pey, cert)
client.configureOfflinePublishQueueing(1)
client.configureConnectDisconnectTimeout(10) # 10 sec
client.configureMQTTOperationTimeout(5) # 5 sec
return client
def handler(signum, frame):
print "Signal handler called with signal", signum
client.disconnect()
GPIO.cleanup()
sys.exit(0)
if __name__ == '__main__':
# Parse command-line arguments.
args = parse()
# Configure logging
configure_logging()
# Create mqtt client.
client = careate_client(
args["endpoint"], args["rootCA"], args["cert"], args["key"])
# Connect.
client.connect()
signal.signal(signal.SIGINT, handler)
GPIO.setmode(GPIO.BCM)
GPIO.setup(9, GPIO.IN)
before = 0
while True:
now = GPIO.input(9)
if before == 0 and now == 1:
# Create message.
message = {"item": "01"}
# Publish.
client.publish("amzorderer", json.dumps(message), 0)
print "message published."
time.sleep(0.1)
before = now
We publish a message (item classification value "01" for purchasing toilet paper) to AWS IoT when GPIO 9 is entered using RPi.GPIO
.
Execute the above script on Rapsberry Pi Zero and check the operation. Specify the AWS IoT endpoint, root CA, client certificate, and private key path as arguments to the script. Although it is described in the README of AWS IoT SDK, the root CA is here. Get from /content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem). What is a client certificate? The private key is included in the zip that you downloaded when you set up AWS IoT.
$ python test.py -e <endpoint> -r <rootCA path> -c <certificate path> -k <private key path>
2016-12-11 08:15:31,661 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Paho MQTT Client init.
2016-12-11 08:15:31,664 - AWSIoTPythonSDK.core.protocol.mqttCore - INFO - ClientID: raspi0
2016-12-11 08:15:31,667 - AWSIoTPythonSDK.core.protocol.mqttCore - INFO - Protocol: MQTTv3.1.1
2016-12-11 08:15:31,672 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Register Paho MQTT Client callbacks.
2016-12-11 08:15:31,675 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - mqttCore init.
2016-12-11 08:15:31,680 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Load CAFile from: root-CA.crt
2016-12-11 08:15:31,683 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Load Key from: raspi0.private.key
2016-12-11 08:15:31,687 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Load Cert from: raspi0.cert.pem
2016-12-11 08:15:31,691 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Custom setting for publish queueing: queueSize = 1
2016-12-11 08:15:31,696 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Custom setting for publish queueing: dropBehavior = Drop Newest
2016-12-11 08:15:31,699 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Set maximum connect/disconnect timeout to be 10 second.
2016-12-11 08:15:31,704 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Set maximum MQTT operation timeout to be 5 second
2016-12-11 08:15:31,710 - AWSIoTPythonSDK.core.protocol.mqttCore - INFO - Connection type: TLSv1.2 Mutual Authentication
2016-12-11 08:15:32,384 - AWSIoTPythonSDK.core.protocol.mqttCore - INFO - Connected to AWS IoT.
2016-12-11 08:15:32,386 - AWSIoTPythonSDK.core.protocol.mqttCore - DEBUG - Connect time consumption: 70.0ms.
When I pressed the tact switch, a message was published to AWS IoT and the purchase of "toilet paper" was successfully completed on Amazon.
Miniaturization of client devices This time, the power supply uses a mobile battery, but I would like to use lithium polymer to reduce the size. I also want to use a small breadboard.
Supports multiple buttons I would like to have multiple buttons, such as purchasing toilet paper when button ① is pressed and purchasing Saran Wrap when button ② is pressed.
Button case creation
Recommended Posts