Run commands/scripts on a Raspberry Pi when a voice command is given to Alexa.
I wanted the ability to be able to run commands on my Raspberry Pi by issuing voice commands to my Alexa Dots in my house. Also I wanted to schedule tasks via Alexa as opposed to having to add scheduled tasks individually to the several Pis I have around the house.
In order to do this, we need to set up:
- Log in in to AWS and the Amazon developer site as well to confirm you can access AWS and the Amazon dev site
- AWS IAM: Create a user and a group that the RPi script will use to log in to AWS
- AWS SQS (message queue).
- AWS LAMBDA function
- Amazon Alexa Skill
An Alexa skill points to a Lambda function and based on what voice command is issued depends on what message is added to SQS queue, on the Rapsberry Pi runs a script to check the SQS queue and dependant on the message it will initiate jobs or scripts on the RPi.
AWS and Amazon dev accounts
If you do not already have an AWS account you will need to set one up, there is no reason why this project should fall outside of the AWS 'free tier' which allows 1 million free requests a month to Lambda and 1 million requests to the SQS service. In order to go past the free tier you would need to make more than 1 request every 3 seconds (based on a 30 day month), once set up I set the script to run every 15 seconds but of course if I had 10 RPi's doing a request then that would need to be factored in.
https://developer.amazon.com/alexa
AWS - Create a user and group
Login to AWS and go to the IAM Management console and add a user:
https://console.aws.amazon.com/iam/home?#/users
As the permissions are set at the group level we need to create a new group for this user, if we create any more users in the future they can also use the same group. This user will log in from the Raspberry Pi and so needs to be able to be able to see and update the queue in AWS SQS.
For this we can use an amazon predefined policy (pre set permissions), search for 'sqs' and select the policy 'AWSLambdaSQSQueueExecutionRole' followed by clicking the 'Create group' (this will allow the user to read and write to the SQS queue).
This will take you back to the 'Add user to group' screen and the new group will be selected to add the new user to, move forward by clicking the 'Next: Tags' button.
On the 'Add tags' screen just move on by clicking the 'Next: Review' button.
On the next screen, review the details for the new user and click 'Create user'
On the following screen make sure you 'Download.csv' as this will be your ONLY opportunity to get the credentials for this user.
The credentials in the CSV will be added to scripts on the RPi so it can log in to access the SQS queue.
NOTE: As of June 2019 in order for an Alexa skill to run a Lambda function, the Lamda code must be hosted in one of the following regions:
- Asia Pacific (Tokyo)
- EU (Ireland)
- US East (N. Virginia)
- US West (Oregon)
https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-an-aws-lambda-function.html
As I live in England (UK) I am going to host by SQS Queue and Lambda function in the Ireland region.
Find the SQS page and create a new Simple queue service:
Enter a new name and create a 'Standard Queue' using the 'Quick-Create Queue' option:
Click on the new queue and take a note of the ARN and the URL that we will need later:
IN AWS, in the 'services' search for 'Lambda' and create a new function:
Select 'Author from scratch', add a name and change the 'runtime' to 'Python 2.7'. For the execution role select 'Create a new role from AWS policy template, select the 'Amazon SQS poller permissions' policy give the role a name and create the function.
For the configuration of the function, when you click on something in the 'designer' panel the configuration settings are seen in the bottom half of the screen.
From the left add the 'Alexa Skills Kit' trigger, configuration is required to be able to save this function. Scroll to the bottom, as we do not have an Alexa Skill ID yet (we have not created a skill yet) we will just 'disable' this for now and come back to it later.
Select the Lambda function icon (for me it is 'RPi-LED-Function) and at the bottom we need to provide our custom python code, this code will provide feedback to the Alexa skill and also update the SQS queue.
I have added some comments in to the code below on what will need to be changed.
import boto3
# Below you need to add in your access key, access secret, rgion and sqs queue url
access_key = "This can be found in the downloaded .csv file"
access_secret = "This can be found in the downloaded .csv file"
region ="eu-west-1"
queue_url = "This can be found when looking at the SQS queue, https://..."
# you should not need to change the following unless you know what your doing.
def build_speechlet_response(title, output, reprompt_text, should_end_session):
return {
'outputSpeech': {
'type': 'PlainText',
'text': output
},
'card': {
'type': 'Simple',
'title': "SessionSpeechlet - " + title,
'content': "SessionSpeechlet - " + output
},
'reprompt': {
'outputSpeech': {
'type': 'PlainText',
'text': reprompt_text
}
},
'shouldEndSession': should_end_session
}
def build_response(session_attributes, speechlet_response):
return {
'version': '1.0',
'sessionAttributes': session_attributes,
'response': speechlet_response
}
def post_message(client, message_body, url):
response = client.send_message(QueueUrl = url, MessageBody= message_body)
def lambda_handler(event, context):
client = boto3.client('sqs', aws_access_key_id = access_key, aws_secret_access_key = access_secret, region_name = region)
intent_name = event['request']['intent']['name']
# The following needs to be customised
# The intent names shown below are linked with intents created in the custom Alexa Skill.
# The 'post_message' relates to the SQS queue
# The 'message' line is the message/response that Alexa will speak back to you
if intent_name == "LightsOn":
post_message(client, 'on', queue_url)
message = "Lounge Lights will now turn on"
elif intent_name == "LightsOff":
post_message(client, 'off', queue_url)
message = "Lounge Lights will now turn off"
elif intent_name == "LightsRed":
post_message(client, 'red', queue_url)
message = "Lounge Lights will change to red"
elif intent_name == "LightsGreen":
post_message(client, 'green', queue_url)
message = "Lounge Lights will now change to green"
elif intent_name == "LightsBlue":
post_message(client, 'blue', queue_url)
message = "Lounge Lights will now change to blue"
elif intent_name == "LightsTest":
post_message(client, 'test', queue_url)
message = "Lounge Lights will now run a test sequence"
else:
message = "Sorry but I do not understand that request"
speechlet = build_speechlet_response("Mirror Status", message, "", "true")
return build_response({}, speechlet)
0
Create the custom Alexa skillLog in here and create a new skill, select the 'custom' skill and 'Start from scratch:'
https://developer.amazon.com/en-US/alexa/alexa-skills-kit
Here you need to configure the following:
Invocation Name - This is the name that will be used to trigger the skill, I have called mine 'lounge lights.'
Intents, Samples, and Slots - For this example I have kept it simple and created an 'intent' for each action as shown below, you should also notice that these intents reflect the code I added in Lambda.
Here is an example of one of my intents, you can of course add whatever you want depending on your requirements:
So, for this utterance to work I would say 'Alexa, ask Lounge Lights to turn off.'
Build Model - Once the above is done, save and build the model.
Endpoint - add an endpoint, this is the ARN of the Lambda function that we have already created. If you get any errors saving this endpoint make sure the following have been checked:
- Lambda code is located in: Asia Pacific (Tokyo), EU (Ireland), US East (N. Virginia) or US West (Oregon)
- The 'Alexa Skills Kit' trigger has been added to the Lamdba function
From the Alexa skill interface, click on the 'test' tab and from here you can test to confirm that 1) your intents are correct and 2) the lambda function is correctly configured. Once testing is complete you can then go on to configure the Raspberry Pi to read from the SQS queue.
Configure Raspberry Pi (linux device)Depending on your end goal depends on what you need to do on your RPi, my first test was to turn on/off my monitor for my Magic Mirror and also to be able to reboot the RPi.
The second was inline with this project writeup and was to be able to switch on a ws2811/ws2812 LED strip of lights connection to my RPi.
The following code needs to be ran, this will log in to AWS and look at the entry in the SQS queue and depending on the message stored depends on what local Python script is ran. To run up the LEDs you will need to run the script as 'sudo.'
import boto3
import os
import time
access_key = "Access key from the csv file"
access_secret = "Access seret from the csv file"
region = "the region where the SQS queue is - found in the queue url"
queue_url = "SQS Queue URL, https://sqs....."
def pop_message(client, url):
response = client.receive_message(QueueUrl = url, MaxNumberOfMessages = 10)
#last message posted becomes messages
message = response['Messages'][0]['Body']
receipt = response['Messages'][0]['ReceiptHandle']
client.delete_message(QueueUrl = url, ReceiptHandle = receipt)
return message
client = boto3.client('sqs', aws_access_key_id = access_key, aws_secret_access_key = access_secret, region_name = nameOfTheRegion
waittime = 20
client.set_queue_attributes(QueueUrl = queue_url, Attributes = {'ReceiveMessageWaitTimeSeconds': str(waittime)})
time_start = time.time()
while (time.time() - time_start < 30):
print("Checking...")
try:
message = pop_message(client, queue_url)
print(message)
if message == "on":
os.system("python /home/pi/LEDScripts/LED_on.py")
elif message == "off":
os.system("python /home/pi/LEDScripts/LED_off.py")
elif message == "blue":
os.system("python /home/pi/LEDScripts/LED_blue.py")
elif message == "red":
os.system("python /home/pi/LEDScripts/LED_red.py")
elif message == "green":
os.system("python /home/pi/LEDScripts/LED_green.py")
elif message == "test":
os.system("python /home/pi/LEDScripts/LED_test.py")
except:
pass
You'll see above that the package 'boto3' is used (AWS SDK), you can install this by running:
python -m pip install boto3
Here is an example of one of the Python scripts which are ran:
from neopixel import *
# LED strip configuration:
LED_COUNT = 12 # Number of LED pixels.
LED_PIN = 19 # GPIO pin connected to the pixels (18 uses PWM!).
LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
LED_DMA = 10 # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 30 # Set to 0 for darkest and 255 for brightest
LED_INVERT = False # True to invert the signal
LED_CHANNEL = 1 # set to '1' for GPIOs 13, 19, 41, 45 or 53
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
strip.begin()
for i in range(strip.numPixels()):
strip.setPixelColor(i, Color(0, 255, 0))
strip.show()
Setting up the Pi for the LED stripsIf you have not run these up before there are different libraries can be ran, however I choose to use this one: https://github.com/jgarff/rpi_ws281x.git
You'll need to run the following:
sudo apt-get install build-essential python-dev git scons swig
git clone https://github.com/jgarff/rpi_ws281x.git
Then from the 'python' directory in the download package from GitHub:
sudo python setup.py build
sudo python setup.py install
Comments