Publishing Alexa's Smart Home Skill for a Maker doesn't need to be hard. In order to prove it, we built dasBridge, a platform which aims to solve address the main pain points in building Alexa Smart Home Devices for the Maker. As such, it includes:
- Boilerplate code for a generic Alexa Smart Home Bridge, supporting a couple of interesting devices (Temperature, Color Lamp, and Smart Relay)
- Boilerplate configuration for AWS, using the Serverless Framework
- Clients using either GoBot or custom Firmware to allow one to integrate their devices to Amazon's IoT Gateway
Samples Include:
- Generic Temperature Sensor (using GoBot + Arduino with Firmata + BMP280)
- Color Lamp (Bare Golang + Custom Arduino Firmware + NeoPixels)
- Generic Smart Relay (ditto, except with Relays)
It was meant to be easily extensible, thus acting as the core fabric for your next Smart Home Skills
The VisiondasBridge is meant to serve as a template for a pure, enterprise grade device gateway. By this, we mean that we've already built the wheel, do you don't have to reinvent it.
(In fact, this still was originally built to be sent straight to Alexa Skills' Certification. However, time constraints held us back - we might reconsider doing it at a later point)
Premises and Design Goals involve:
- Using Arduino as a gateway to the underlying hardware whenever possible, and leave the middleware to Raspberry. Considering the price for an Arduino Board + a Raspberry Pi (W or not) + card + USB Power, it looks like the 'IoT Tax' (read: the cost to connect a device) for the average maker could be safely around USD 20 per device, which is acceptable
- Using TypeScript to ensure correctness on the server side with an Average Performance and Good Productivity, while Golang + Gobot + Firmata on the Device Side will work as a good prototyping environment, offering both performance, portability and performance - while with average productivity levels
- Rely as much as possible on an AWS Serverless Stack in order to heavily reduce costs. On the worst case, rely on CloudWatch Period Events, and/or AWS Step Functions. At the same time, leverage as much as possible from the Device Shadow API, while ensuring enough isolation
In order to build this, its required to the user the ability to:
- Use git and github (e.g. clone a repo, commit)
- Basic node.js skills (installing node and running npm), regardless of windows and Linux
- An Amazon Web Services Account to deploy the code, as well as a developer.amazon.com account
- Basic Arduino Skills (assemble a circuit, compile and upload a sketch)
- Basic Raspberry Pi and Linux skills (install a package, open a shell session and run commands and download/unpack packages)
- Be able to install the Go Language on the Raspberry Pi
Prerequisites:
- Machine with both git and nodejs installed - desktop recommended, regardless of being Windows or Linux
- awscli installed. See https://docs.aws.amazon.com/cli/latest/userguide/installing.html for a guide on how to install it
- AWS Account, with Access Key Id and Secret Access Key, Administrator Group Access Level, with awscli configured under the us-east-region. See https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html for a good overview of the Process
On the examples below, we'll be using Linux commands, but it shouldn't be hard to translate those to Windows. If you've got problems, feel free to address in the comments below.
On the machine, clone the dasBridge skill code:
git clone https://github.com/dasbridge/dasbridge-skill.git
Then, go to the dasBridge directory and run npm install:
cd dasbridge
npm install
Go to the 'data' directory and run the 'serverless deploy' command:
cd data
../node_modules/.bin/serverless deploy
Go back to its parent directory and copy the data/stack.json file to src/config/stack.json, and then run the 'serverless deploy' command again:
cd ..
cp data/stack.json src/config/stack.json
serverless deploy
After completion, inspect the results of stack.json of the current directory. Copy the value of the 'SkillLambdaFunctionQualifiedArn' parameter - the first line shown below:
$ cat stack.json
{
"SkillLambdaFunctionQualifiedArn": "arn:aws:lambda:us-east-1:005368163414:function:db-dev-skill:28",
"ModelLambdaFunctionQualifiedArn": "arn:aws:lambda:us-east-1:005368163414:function:db-dev-model:10",
"ProvisionerLambdaFunctionQualifiedArn": "arn:aws:lambda:us-east-1:005368163414:function:db-dev-provisioner:28",
"StatusLambdaFunctionQualifiedArn": "arn:aws:lambda:us-east-1:005368163414:function:db-dev-status:16",
"ServiceEndpoint": "https://t2p7v4dasdas.execute-api.us-east-1.amazonaws.com/dev",
"ServerlessDeploymentBucketName": "db-dev-serverlessdeploymentbucket-166syk9vv046z"
}
Now, hop on to the Amazon Developer Website, https://developer.amazon.com/, authenticate/register account. Lets start with a Security Profile:
Login with AmazonMapping users to things requires us to also map to Amazon Users. So we need to create a Login With Amazon (LWA) Configuration and a Security Profile. Open a new tab to https://developer.amazon.com/lwa/sp/overview.html and click on "Create a New Security Profile", then:
- Security Profile Name: dasBridge
- Security Profile Description: dasBridge lwa profile
- Consent Privacy Notice URL: https://example.com/legal/privacy
Save. Copy the ClientId and Client Secret, but keep this tab open since we'll need to edit it further.
Alexa Skills KitFrom the Developer Console, select "Alexa" | "Alexa Skills Kit" | "Add a New Skill", and then fill the values below (unless otherwise specified, accept the defaults):
Page: "Skill Information"
- Skill Type: Smart Home Skill API
- Name: dasBridge
- Click Save
Copy the value of the "Application Id" field, the 'amzn1.ask.skill.xxx' string.
Now, open the another tab and open the AWS Console, check the 'N. Virginia' region is selected, and open the Lambda page. Find the 'db-dev-skill' Lambda function and click it.
On the next screen, click on 'Alexa Smart Home' (under 'Add Triggers'), then under 'Configure triggers, paste the string for the 'Application Id' you got two paragraphs ago, and click on 'Add'. See the screen below.
Once you do it, don't forget to click on the Yellow 'Save' button which will appear once you clicked 'Add':
Now go back to the Amazon Developer Page for the Alexa Skills Kit. Under the 'Configuration' section, paste the value of the "SkillLambdaFunctionQualifiedArn" without the trailing ':NUMBER'. e.g., if you value is
arn:aws:lambda:us-east-1:005368163414:function:db-dev-skill:28
Select so you paste only this value (notice I removed the trailing colon-and-number):
arn:aws:lambda:us-east-1:005368163414:function:db-dev-skill
Copy this value and paste it under the 'Default' section, and then:
Configuration Section:
- Provide geographical region endpoints? No
- Authorization URL: https://www.amazon.com/ap/oa
- Scope / Add Scope: profile
- Authorization Grant Type: Auth Code Grant
- Access Token URI: https://api.amazon.com/auth/o2/token
Now, paste the values from the Client Id and Client Secret from the tab for the LWA Security Profile.
Copy the values of the 'Redirect URL's' , switch to the LWA Profile Tab, and paste it into their 'Web Settings' | 'Allowed Return URLs'. Save and close this tab (wheew):
Back to the Skill Configuration, now you can save it. Here's a picture to help you validate it:
Skip the 'Publishing Information' and jump to the 'Private and Compliance Section'. Fill with those values:
- Does this skill allow users to make purchases or spend real money? No
- Does this Alexa skill collect users' personal information? Yes
- Is this skill directed to or does it target children under the age or 13? No
- Export Compliance? <check>
- Does this skill contain advertising? No
- Privacy Policy URL: https://example.com/legal/privacy
Save. While its not ready to submit for certification (remember we skipped the 'Publishing Information' section), its ready for us to use
Enabling the SkillOpen the Alexa App on your phone, then 'Skills' | 'Your Skills' | 'Dev Skills', click on 'dasBridge' and then 'Enable'. The rest of this flow should be self-explanatory.
If you want to troubleshoot this step, besides AWS, here are two other things you can look up:
Smoketailsmoketail (https://npmjs.com/package/smoketail) is tail -f, but for lambdas. Lambdas write into a thing called AWS CloudWatch Logs. Use this command to troubleshoot the lambda skill:
$ ./node_modules/.bin/smoketail -f /aws/lambda/db-dev-skill
DynamoDBIf you sucessfully managed to write into the table, the DynamoDB table should have an entry with your user id (application-scoped) and name / email under the db-dev-dbCustomerTable-* DynamoDB Table:
On a product built based on dasBridge, you could use CustomerService (src/customer.ts) to validate and/or send notifications for newer users to finish their setup, for instance.
If you've made this far, rejoice! You just made the hardest part of the whole process: Configuring the Lambdas, Skill and LWA Profiles all at once. From now on, everything gets easier to develop!
AWS IoT ConfigurationAWS has the AWS IoT Service. Its more than a platform than a service. Among other things, it manages:
- Device Provisioning, Metadata and Authentication Service (X509 Keys, CA's, Thing Registry)
- Authorization Layer (IAM, Policies)
- Taxonomy (Device Groups)
- The Device Shadow API
- Messaging Layer (MQTT / MQTT over WebSockets)
- Jobs and Greengrass
As for the first four items above, they're modelled as such:
Some notes:
- A Device Shadow is one attribute of a Thing. There's also a Thing Name - which is going to be critical for our policy below
- A Certificate might belong to an AWS-managed Certificate Authority, of one you brought by creating your own root CA
- If you're using the AWS-managed CA, AWS has its own root certificate (and some sub certificates) in order to allow you to use.
So, in order to provision a device (read: 'Thing'), we need at least a Thing Type and a Policy. Creating a new Thing becomes a matter of creating the certificate and the thing, and binding those together into their Types and Policies.
Right now, we'll focus on the basic configuration so the lambdas can have the right security to only manage their device shadows, as well as being able to connect using their own certificates.
From the directory you cloned the dasbridge-skill code, open the misc/policy.json on a text editor, and replace the string '235367163414' with your AWS Account Id, a 12-digit number which could be obtained with this command:
aws sts get-account-id
For example, from another AWS account, the 12-digit number is the 'Account' value:
{
"Account": "062880515026",
"UserId": "062880515026",
"Arn": "arn:aws:iam::062880515026:root"
}
Now from the AWS Console, open the IoT service console and select 'Secure' | 'Policies', then create a new policy called 'db-default-policy' with its contents (switch to 'Advanced Mode' in order to paste the JSON). Remember to save.
Now from 'Manage' | 'Types', create a new Thing Type called 'db-default-device' with those 'user_id' as one of their (indexed) attribute.
Check twice before saving: In order to delete, you need to first deprecate and wait 5 minutes before deleting the thing type, so its wise to double check everything.
It should look like this:
Now, on DynamoDB, select the db-data-dev-dbThingsType-* table add a new record with these parameters:
- thing_type: 'db-default-thing-type'
- thing_policy (click on +, then String): 'db-default-policy'
This basically map that our API, when it requests to provision a new device with type 'db-default-device', it gets created with this thing_policy.
So far so good. We now had the basic infrastructure for the lambda ready to use for our clients. There's just one step left to start connecting and building things into Alexa Smart Home
Building the Example Clients and Requesting Configuration FilesDasBridge Users (like you, or your significant other which you can entrust to manage your home appliances) needs some access to manage it. As such, there are two lambdas which are paired with cli tools to generate device configuration files. Those device configuration files are json files with all the required information to setup an AWS API Gateway Configuration. For instance, here's one valid confirmation file:
{
"rootCertificates": {
"https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem": "-----BEGIN CERTIFICATE-----\nMIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\nyjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\nExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\nU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\nZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\naG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\nMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\nZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\nbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\nU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\naXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\nnmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\nt0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\nSdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\nBO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\nrCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\nNIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\nBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\nBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\naXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\nMzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\np6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\n5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\nWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\n4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\nhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\n-----END CERTIFICATE-----"
},
"endpoint": "a3slywy094gyae.iot.us-east-1.amazonaws.com",
"thingPolicy": "thingDefaultPolicy",
"certificateArn": "arn:aws:iot:us-east-1:235368163414:cert/187898e723f64afaef9ecae462aef6ac2b2ba8b8a2c50c90b3693dc129a1d2e6",
"certificateId": "187898e723f64afaef9ecae462aef6ac2b2ba8b8a2c50c90b3693dc129a1d2e6",
"certificatePem": "-----BEGIN CERTIFICATE-----\nMIIDWTCCAkGgAwIBAgIUWMzDF26wZ6/8TOAI5hnDhNXKxl4wDQYJKoZIhvcNAQEL\nBQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g\nSW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE4MDEyMDIyMjkx\nNFoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0\nZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALSCJttpCIiGKIEfC9nz\nWtBvBforBJGZOU1y1Zq3tweSXsmlw2zEAv5o07OAm0ZgrD1gFXpb9dTtEWrPA/kv\n67o/ku3WPFqJtnoM1wixErUqMWtzfGcBlYRFy5HJVVpDaU8kc4WL6XzOMnuTtohj\nMQsJ5NIlzjSMOLQOkQxq/EekuNy7V2ttc/0hgiGynUg2TYT3oXLMtbuA2RtlOTFG\nmY1ygkNmDZ5W8c26/nVA487rty3oP11oI8t73Ephngu7d5cTuVkXSyudr1e9yBzS\ni1WMzAAerjrx/RAM2c5tY9AEtJS23o5GfMWnmggcVv+EoGxWEu/ipmkSTKbQV5Zr\n/0kCAwEAAaNgMF4wHwYDVR0jBBgwFoAUQXqasaL3a3YfdoNZLXNYBVYJWdAwHQYD\nVR0OBBYEFPofk/kQFyWrxx5mIoKpIY8hzndfMAwGA1UdEwEB/wQCMAAwDgYDVR0P\nAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQCcA2tKUMPTCrmme9lK37aAARCw\nBcdtwhc9/zXTkGAQrVEl6bwmqqKb+I8ykYw6t6NNuJs4y1CnfP4rJloOk0Ui9/Ra\nqzAwTWP19WUX9Lj1fRDs33KKc3JcmN4Mbb/IAKzmOHYOsS1BCzzcF+O3XggRLiao\nbie/6d7yywzKWCJoeunskMdY6O5uN8SLyJO0nk0myMpteMIPq4xdh+yNh2w7yKBs\nuPFhNLCYpRXCwlbnVSKrL0LEb8tErZ7FIh2RuYAdRZmdLHyVU0zaibOM8e95/mTS\n73OiXlN+xiZdOaBJ8s20BpKEpPkT7wsabW2kvfZRgIKwOC8D6VR736YCLZrH\n-----END CERTIFICATE-----\n",
"publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtIIm22kIiIYogR8L2fNa\n0G8F+isEkZk5TXLVmre3B5JeyaXDbMQC/mjTs4CbRmCsPWAVelv11O0Ras8D+S/r\nuj+S7dY8Wom2egzXCLEStSoxa3N8ZwGVhEXLkclVWkNpTyRzhYvpfM4ye5O2iGMx\nCwnk0iXONIw4tA6RDGr8R6S43LtXa21z/SGCIbKdSDZNhPehcsy1u4DZG2U5MUaZ\njXKCQ2YNnlbxzbr+dUDjzuu3Leg/XWgjy3vcSmGeC7t3lxO5WRdLK52vV73IHNKL\nVYzMAB6uOvH9EAzZzm1j0AS0lLbejkZ8xaeaCBxW/4SgbFYS7+KmaRJMptBXlmv/\nSQIDAQAB\n-----END PUBLIC KEY-----\n",
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAtIIm22kIiIYogR8L2fNa0G8F+isEkZk5TXLVmre3B5JeyaXD\nbMQC/mjTs4CbRmCsPWAVelv11O0Ras8D+S/ruj+S7dY8Wom2egzXCLEStSoxa3N8\nZwGVhEXLkclVWkNpTyRzhYvpfM4ye5O2iGMxCwnk0iXONIw4tA6RDGr8R6S43LtX\na21z/SGCIbKdSDZNhPehcsy1u4DZG2U5MUaZjXKCQ2YNnlbxzbr+dUDjzuu3Leg/\nXWgjy3vcSmGeC7t3lxO5WRdLK52vV73IHNKLVYzMAB6uOvH9EAzZzm1j0AS0lLbe\njkZ8xaeaCBxW/4SgbFYS7+KmaRJMptBXlmv/SQIDAQABAoIBAQCCO0CznjT04u4I\nMpEHX0cPCHrkgfLa5RRtp0MrTA62XMkcbQbwyv5p9NGAOXJNtWOvKEp2rDBRvfSJ\n/e+c8hD9n+5uv9ltbNwFdIwUDkU0Bcv//SMs4fywoPZ9KSf2Zpps6hyaplX/oHjV\npb3U6tOwZiP75iC+0sf6SGQn9GwtdxiYlMRwlBFVUcxlWdEYKz6Id2xVedQTzfEu\n1e4xxUp7QAMpUkkBGOY0gdTJbuW7Hl0HgvjFqMD0diIaq/BqYL9A0JrOfWyBojp/\n6fT+QlzDBqkD7bIHTynfgtubP0us80pdK66c/jMXTNLAeZahTocSwZjJWsK1p4XE\nsbDR6pRNAoGBAObzFJq+Seefi4my7g356JRYG1c1EpqVE0CBrvQcEwc7PKEdsAXK\nEOH2qF0jMqpmYvmid44qIU+7rldhf2VOIEHtX1maD5eTrqOVMjFo73oHVbLdmYVp\nHzCNkJV4uz/wFz93DWJx4mDyDYL5Ur1B0yYI1lljuXEU6GyEA8XL9t8zAoGBAMgW\ncOtv3Q+rOY/xbYCYdc5sElXxF+jdbD4bywtEH68Oi+MqPYZYqZQsCEbPTdypsZFy\nVwWauR0SN5HS2mBAd4FCKf6LXzNYyg+N9cMFsDY6c3oqQ5f3xqdgG02hYE4HRdZa\nQJBk7+Gl8F7gKJN2CQ5ml5v92di0ph+4HtdM8teTAoGBAJkpZmXFgu/YcEru3k9o\niGUxFVi9eswHx4/FMYj0wW3PTEMxlBYSzyV8VmYGPMijREp4A7/Fkvf06QdMQpel\nu2DdkpmywVpt96jG/lWkWQVdrekEa5b9g1DI4bUvL0eU62YMh1TdYBuZC3rr7IJV\nEDouLDF2IHqtOgMVfgcEYIHfAoGAerrBIrjn6L+HPFLDbxiVUjuZ95s9pjmDNomH\nvu5XXOJYTcvQF0L1KYzkusJXqR1xbJys4iQonbtZ2Jl0F4wTgXabGaZVi8JPlsDl\n2Wz4zBqIUIvyEBoMgQgDV1fAIbx2SufrKGaxeUB2s0tduC2zmmC1aBqKcFFGgLhI\nOpnZbv8CgYEAyekW3TcBmwhZUXFihbqZ565OeyhqyOUiixZmXRVVkb5Ox81wgjfv\nDE8raSQ2WGERG14IDvo1eb2tFvtNXuaXGs02ZvbA2T6B7CaGTBrUKjEubSB6VUao\nzSJD40T5KWBDmW0V6GZNz5E8QyVz2vFCEap3HEKdSjQF8x5nlWbPHCo=\n-----END RSA PRIVATE KEY-----\n",
"thingArn": "arn:aws:iot:us-east-1:235368163414:thing/sample01",
"thingId": "0e67d5ba-261b-48de-85b5-c0d72e403599",
"thingName": "sample01"
}
So, in order to connect into AWS IoT Gateway via MQTT (and via TLS), you need:
- A DNS Endpoint
- A Certificate and its Private Key
- A Root Certificate to validate the other endpoint
- A Thing Name (in order to figure out which topic to publish/subscribe for the Device Shadows API)
Most of those data (Except private key) are also stored in DynamoDB, thus making it easier to troubleshoot it.
The file above contains all those data, plus some extra metadata thus making it easy for you to troubleshoot. However, in order to manage those device files (generate and remove), its interesting to add another indirection layer.
Enter API Keys.
Since dasBridge is deployed as a serverless application, you, as a power AWS User, can generate API Gateway Keys which, in turn, can allow you to easily create and remove devices under the AWS IoT using an HTTPS RESTful endpoint for it.
So we'll just run an utility to generate an API Key, configure it and return it to you, so we can move into the Raspberry Pi and properly use those files.
Provided you've got Linked the e-mail user@example.com (which is your AWS Account email), simply run from your project directory:
$ # Compile a local version of the tools
$ ./node_modules/.bin/tsc
$ node ./build/src/tools/generateApiKey.js user@example.com
email: user@example.com
customerKeysTable: db-data-dev-dbCustomerKeysTable-1JIDLW5TPMR2R
customerTable: db-data-dev-dbCustomerTable-1CXQ2BCFY8G59
ServiceEndpoint: https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev
restApiId: t2p7v4dvcf
restApiStage: dev
item: {
"last_updated": 1518563648,
"user_id": "amzn1.account.BLAHBLAHBLAHBHASLDADSA",
"email": "user@example.com",
"short_id": "BLAHBLAHBLAHBHASLDADSA",
"name": "Sample User"
}
apiKey: {
"id": "hCjiUbAnRG14deL1IbdB2rHe9QeFI7q4VexKLb",
"value": "hCjiUbAnRG14deL1IbdB2rHe9QeFI7q4VexKLb",
"enabled": true,
"createdDate": "2018-02-14T00:23:42.000Z",
"lastUpdatedDate": "2018-02-14T00:23:42.000Z",
"stageKeys": [
"t2p7v4dvcf/dev"
]
}
From now on, the key with id raKkBARg6eaPTiMdaIplS3ZssqOR3qRC9OBopzZt
can now be used to issue requests to the dasBridge endpoint at https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev
to manage your devices.
This endpoint is key-protected and returns a status, so this command will fail since there's not an API Key sent:
$ curl https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev
{"message": "Forbidden"}
While that when you supply the key with the X-Api-Key
parameter on curl, and if its valid, it should return this:
$ curl -H'X-Api-Key: hCjiUbAnRG14deL1IbdB2rHe9QeFI7q4VexKLb' https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev
{
"name": "Sample User",
"email": "user@example.com"
}
Keep note of the Endpoint, and your API Key. We'll use that in a moment.
Compiling the CLI Tools on the Raspberry PiFor this part, we'll be based on Raspbian and deb-based distros (it should work on Ubuntu, for instance). YMMV.
Configure the Raspberry Pi as usual, and then log on on it. First, install git (for go get
):
apt-get -y install git
Now, open the go downloads page and click on downloads. Select the armv6l
version and copy its URL, then run wget:
wget https://dl.google.com/go/go1.10.linux-armv6l.tar.gz
Create a share/ dir and unpack it there:
mkdir share
cd share
tar ~/go1.10.linux-armv6l.tar.gz
mkdir
Edit ~/.bashrc and append these lines. Replace the first two lines with the results from the previous section:
export DB_ENDPOINT=[ENDPOINT URL]
export DB_API_KEY=[API KEY]
export GOROOT=$HOME/share/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
Reload your environment:
source ~/.bashrc
Test by fetching gb, which is a required dependency:
go get github.com/constabulary/gb/...
which gb
It should display the path to gb: /home/pi/go/bin/gb
Now we'll install some sample clients. Start by compiling the current project:
gb build
Now start using the CLI tools for dasBridge:
./bin/db-device-check
INFO[0000] Using endpoint:https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev/
Response: {
"name": "Sample User",
"email": "user@example.com"
}
This will validate your API Keys. If everything is fine, register a new client:
./bin/db-device-register myShinyDevice db-default-thing-type
INFO[0000] Using endpoint: https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev/ (in fact: https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev/device)
Response: {"rootCertificates":{"https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem":"-----BEGIN CERTIFICATE-----\nMIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\nyjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\nExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\nU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\nZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\naG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\nMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\nZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\nbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\nU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\naXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\nnmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\nt0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\nSdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\nBO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\nrCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\nNIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\nBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\nBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\naXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\nMzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\np6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\n5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\nWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\n4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\nhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\n-----END CERTIFICATE-----"},"endpoint":"a3slywy094gyae.iot.us-east-1.amazonaws.com","thingPolicy":"db-default-policy","certificateArn":"arn:aws:iot:us-east-1:235368163414:cert/e768e1928dec019b3d748316572d17e2ae4531d21b6d126782976261799e1401","certificateId":"e768e1928dec019b3d748316572d17e2ae4531d21b6d126782976261799e1401","certificatePem":"-----BEGIN CERTIFICATE-----\nMIIDWjCCAkKgAwIBAgIVANAk5wk9DWkumIcIhef6ZCh6SxorMA0GCSqGSIb3DQEB\nCwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t\nIEluYy4gTD1TZWF0dGxlIFNUPVdhc2hpbmd0b24gQz1VUzAeFw0xODAyMTgxNTM0\nMTNaFw00OTEyMzEyMzU5NTlaMB4xHDAaBgNVBAMME0FXUyBJb1QgQ2VydGlmaWNh\ndGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDX7o+q4LZO/9+OOBDF\ncsUQV/xJjVddmbO0hs5wM5VPaqmoLIYorHPc0sJhN9+LibcXvPbCE+fOMlkMCuI8\nfrRiDiC7+ik8ns+Nbqa5/zXyO2pYmGwqgPEYFeX3v66qmcl9YOHFrAbFAA9x4Ng3\n5k/TWvMIFS1G7KWH4iHPV3ecTX1YcngoQI8joZOoUz35e/7jUkH7+fXaGLsCIBTc\ndmg+fyP6VGH0tIqRxptar3UIUEjnYS/Wr6qmyRCr2r5PIOq2+7g2jJNfyISuXcAd\n6ejlwX2F2qVGZlmCVRYIAaRAYhrFPWUYFu67zZ+9R5VbGclm6CoBHLHha5pAIVPR\nJxdTAgMBAAGjYDBeMB8GA1UdIwQYMBaAFDynll3zULGOIZTKNhFgX4GdiAccMB0G\nA1UdDgQWBBQGt3ef///BerXAq7WlD5SyJfnktzAMBgNVHRMBAf8EAjAAMA4GA1Ud\nDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAeFkYGshcNuHNTXIVk6d8JdU0\n+5RA9acaGB9xFX5Q9er8rubyHwyBmGZxsJ6cUQF41fmqn8e6QIEWbg5t3+hIYD0u\nM0hXUOnNppLVYce7Rn+JCvxQDcVbBCXwvU6ROZ5HyIyfmNdM56kEtAHm1qWdIZ70\ngi7zhHhi0GziKo7FmY2mFvvSLw6Iw66qfNZEjrNatfiPreqnserwFCFt6Yh8sj3i\n9yEQB1ttnxmVm89VEayuJwvdJhIlar6DIoUm3nMjYVixV0ev5GMbg/ej8CB8wMjF\nexSJ3aHGMaMpVqRHIzFprpLpBoKAPtK9tpJHlVIsi4BqYHgR9BCUMLBuIG4DZQ==\n-----END CERTIFICATE-----\n","publicKey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1+6PquC2Tv/fjjgQxXLF\nEFf8SY1XXZmztIbOcDOVT2qpqCyGKKxz3NLCYTffi4m3F7z2whPnzjJZDAriPH60\nYg4gu/opPJ7PjW6muf818jtqWJhsKoDxGBXl97+uqpnJfWDhxawGxQAPceDYN+ZP\n01rzCBUtRuylh+Ihz1d3nE19WHJ4KECPI6GTqFM9+Xv+41JB+/n12hi7AiAU3HZo\nPn8j+lRh9LSKkcabWq91CFBI52Ev1q+qpskQq9q+TyDqtvu4NoyTX8iErl3AHeno\n5cF9hdqlRmZZglUWCAGkQGIaxT1lGBbuu82fvUeVWxnJZugqARyx4WuaQCFT0ScX\nUwIDAQAB\n-----END PUBLIC KEY-----\n","privateKey":"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA1+6PquC2Tv/fjjgQxXLFEFf8SY1XXZmztIbOcDOVT2qpqCyG\nKKxz3NLCYTffi4m3F7z2whPnzjJZDAriPH60Yg4gu/opPJ7PjW6muf818jtqWJhs\nKoDxGBXl97+uqpnJfWDhxawGxQAPceDYN+ZP01rzCBUtRuylh+Ihz1d3nE19WHJ4\nKECPI6GTqFM9+Xv+41JB+/n12hi7AiAU3HZoPn8j+lRh9LSKkcabWq91CFBI52Ev\n1q+qpskQq9q+TyDqtvu4NoyTX8iErl3AHeno5cF9hdqlRmZZglUWCAGkQGIaxT1l\nGBbuu82fvUeVWxnJZugqARyx4WuaQCFT0ScXUwIDAQABAoIBAQCFcWudyXNBDQXm\nCSXsL8ozGHzUI6ILOTKCbRDk7CvowV0JjkJ2nmSX4jO4CuR+gmQBKolAVTbbCehX\n9d3sTs1BD8QRBz82tFpF6EznAx4ejbNh/whRmA/mt5m/6tiRm6qWbin4lCA23Juu\n36ofmZhZYIpyw0uQ2ixN9mS4kzBbCbD8zsc+cP/4Ex+9O3nZd3p64MZjCvMRmH18\nhohrjVCHB9hG+cO2GIVKQMJh0Aj4r/V7p6kIfpZ0oUWCnSk2pZ8178neuzdtlJQt\nENyaDMVuOcU+Mr06B4mT/C/9C/rZdUD+VfL7ihEftWxuJBiuAoj+L94SdGXp03Zi\nouss4Mm5AoGBAPz7sPb9He5m8aC2YSubol1/rbX0cKQTuM8k9jbPylr5CuFcmiMt\n/XhcVes76zRlxkyKGgEB3uKkc32NNrHjH0xY5eIm8tRC7ZRq+IYQxFOawodHbPqw\nYBcn5BUYecsmuZ22Lu3gqqGgkEGSZ3JN/RTilCPTqi30rRYVkhB6QbIFAoGBANqB\nwm7zUh65MPiTIBy2045zI8vaHn2H05eQGY9ax7/NDxSqMSK+hZh1VLIAC3OiFZfK\nLCT2qODRJ1MDuPPd3u5I2oduzeyKtHV24nnkbpXzSWy1ho4bF5MN6qXyIPOu0QVq\nFUpI8a9911rlYyZhsjY1oOCi1bfswsM1qDZqJKt3AoGASNNjpBZFcYWs9SjLXDRX\nTzccGI8fhfwvbWIkhq7Wf83Fdg6kSJBKsUVTbn43PUQ3C32N8tBJC1Fv9aqbHiZV\nsjK9KpcvvC7wGZOZq25UNgUNDshe9OOlJcVUAUakePjS4jW52LyeIh0IqfetU9hj\njImVP6MFEGmpEfxvqdKn6skCgYAfjc1+KDUbqrKfst9YRi6Wy5nHvl/Y0NyFbpHC\nWeGumPYsjcc6tTilo4vviIIe2LE1kkR9sgEBNjLvkgILdWbUEkE8fYvPNNiKffiG\nka5GiTN5N12O9+qXmdLg0+5eQkh896zLZW1BYnLO9YEz8bfCjEwHJHyoatBVUFJS\nSVGyUQKBgC00Jo563DAqVrP7nv0VIvyTmr4m7TymX/8K8g+OY9uvJEGUpMndEoOn\nIRTFZeM5nTjg2sqkqt1PseFMkHzJEsVNNm6tZ/La3hpmn6f7b5fBjmItDoHYHxW0\nFMiug2+9P6RzDukbkeWQeEL0kQgd3UYuqgNZYmXV/h83zWJoHckw\n-----END RSA PRIVATE KEY-----\n","thingArn":"arn:aws:iot:us-east-1:235368163414:thing/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice","thingId":"83a64854-453d-4165-812b-20a97343f356","thingName":"AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice"} (200)
INFO[0002] Response Content: {"rootCertificates":{"https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem":"-----BEGIN CERTIFICATE-----\nMIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\nyjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\nExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\nU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\nZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\naG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\nMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\nZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\nbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\nU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\naXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\nnmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\nt0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\nSdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\nBO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\nrCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\nNIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\nBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\nBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\naXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\nMzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\np6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\n5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\nWE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\n4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\nhnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\n-----END CERTIFICATE-----"},"endpoint":"a3slywy094gyae.iot.us-east-1.amazonaws.com","thingPolicy":"db-default-policy","certificateArn":"arn:aws:iot:us-east-1:235368163414:cert/e768e1928dec019b3d748316572d17e2ae4531d21b6d126782976261799e1401","certificateId":"e768e1928dec019b3d748316572d17e2ae4531d21b6d126782976261799e1401","certificatePem":"-----BEGIN CERTIFICATE-----\nMIIDWjCCAkKgAwIBAgIVANAk5wk9DWkumIcIhef6ZCh6SxorMA0GCSqGSIb3DQEB\nCwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t\nIEluYy4gTD1TZWF0dGxlIFNUPVdhc2hpbmd0b24gQz1VUzAeFw0xODAyMTgxNTM0\nMTNaFw00OTEyMzEyMzU5NTlaMB4xHDAaBgNVBAMME0FXUyBJb1QgQ2VydGlmaWNh\ndGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDX7o+q4LZO/9+OOBDF\ncsUQV/xJjVddmbO0hs5wM5VPaqmoLIYorHPc0sJhN9+LibcXvPbCE+fOMlkMCuI8\nfrRiDiC7+ik8ns+Nbqa5/zXyO2pYmGwqgPEYFeX3v66qmcl9YOHFrAbFAA9x4Ng3\n5k/TWvMIFS1G7KWH4iHPV3ecTX1YcngoQI8joZOoUz35e/7jUkH7+fXaGLsCIBTc\ndmg+fyP6VGH0tIqRxptar3UIUEjnYS/Wr6qmyRCr2r5PIOq2+7g2jJNfyISuXcAd\n6ejlwX2F2qVGZlmCVRYIAaRAYhrFPWUYFu67zZ+9R5VbGclm6CoBHLHha5pAIVPR\nJxdTAgMBAAGjYDBeMB8GA1UdIwQYMBaAFDynll3zULGOIZTKNhFgX4GdiAccMB0G\nA1UdDgQWBBQGt3ef///BerXAq7WlD5SyJfnktzAMBgNVHRMBAf8EAjAAMA4GA1Ud\nDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAeFkYGshcNuHNTXIVk6d8JdU0\n+5RA9acaGB9xFX5Q9er8rubyHwyBmGZxsJ6cUQF41fmqn8e6QIEWbg5t3+hIYD0u\nM0hXUOnNppLVYce7Rn+JCvxQDcVbBCXwvU6ROZ5HyIyfmNdM56kEtAHm1qWdIZ70\ngi7zhHhi0GziKo7FmY2mFvvSLw6Iw66qfNZEjrNatfiPreqnserwFCFt6Yh8sj3i\n9yEQB1ttnxmVm89VEayuJwvdJhIlar6DIoUm3nMjYVixV0ev5GMbg/ej8CB8wMjF\nexSJ3aHGMaMpVqRHIzFprpLpBoKAPtK9tpJHlVIsi4BqYHgR9BCUMLBuIG4DZQ==\n-----END CERTIFICATE-----\n","publicKey":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1+6PquC2Tv/fjjgQxXLF\nEFf8SY1XXZmztIbOcDOVT2qpqCyGKKxz3NLCYTffi4m3F7z2whPnzjJZDAriPH60\nYg4gu/opPJ7PjW6muf818jtqWJhsKoDxGBXl97+uqpnJfWDhxawGxQAPceDYN+ZP\n01rzCBUtRuylh+Ihz1d3nE19WHJ4KECPI6GTqFM9+Xv+41JB+/n12hi7AiAU3HZo\nPn8j+lRh9LSKkcabWq91CFBI52Ev1q+qpskQq9q+TyDqtvu4NoyTX8iErl3AHeno\n5cF9hdqlRmZZglUWCAGkQGIaxT1lGBbuu82fvUeVWxnJZugqARyx4WuaQCFT0ScX\nUwIDAQAB\n-----END PUBLIC KEY-----\n","privateKey":"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA1+6PquC2Tv/fjjgQxXLFEFf8SY1XXZmztIbOcDOVT2qpqCyG\nKKxz3NLCYTffi4m3F7z2whPnzjJZDAriPH60Yg4gu/opPJ7PjW6muf818jtqWJhs\nKoDxGBXl97+uqpnJfWDhxawGxQAPceDYN+ZP01rzCBUtRuylh+Ihz1d3nE19WHJ4\nKECPI6GTqFM9+Xv+41JB+/n12hi7AiAU3HZoPn8j+lRh9LSKkcabWq91CFBI52Ev\n1q+qpskQq9q+TyDqtvu4NoyTX8iErl3AHeno5cF9hdqlRmZZglUWCAGkQGIaxT1l\nGBbuu82fvUeVWxnJZugqARyx4WuaQCFT0ScXUwIDAQABAoIBAQCFcWudyXNBDQXm\nCSXsL8ozGHzUI6ILOTKCbRDk7CvowV0JjkJ2nmSX4jO4CuR+gmQBKolAVTbbCehX\n9d3sTs1BD8QRBz82tFpF6EznAx4ejbNh/whRmA/mt5m/6tiRm6qWbin4lCA23Juu\n36ofmZhZYIpyw0uQ2ixN9mS4kzBbCbD8zsc+cP/4Ex+9O3nZd3p64MZjCvMRmH18\nhohrjVCHB9hG+cO2GIVKQMJh0Aj4r/V7p6kIfpZ0oUWCnSk2pZ8178neuzdtlJQt\nENyaDMVuOcU+Mr06B4mT/C/9C/rZdUD+VfL7ihEftWxuJBiuAoj+L94SdGXp03Zi\nouss4Mm5AoGBAPz7sPb9He5m8aC2YSubol1/rbX0cKQTuM8k9jbPylr5CuFcmiMt\n/XhcVes76zRlxkyKGgEB3uKkc32NNrHjH0xY5eIm8tRC7ZRq+IYQxFOawodHbPqw\nYBcn5BUYecsmuZ22Lu3gqqGgkEGSZ3JN/RTilCPTqi30rRYVkhB6QbIFAoGBANqB\nwm7zUh65MPiTIBy2045zI8vaHn2H05eQGY9ax7/NDxSqMSK+hZh1VLIAC3OiFZfK\nLCT2qODRJ1MDuPPd3u5I2oduzeyKtHV24nnkbpXzSWy1ho4bF5MN6qXyIPOu0QVq\nFUpI8a9911rlYyZhsjY1oOCi1bfswsM1qDZqJKt3AoGASNNjpBZFcYWs9SjLXDRX\nTzccGI8fhfwvbWIkhq7Wf83Fdg6kSJBKsUVTbn43PUQ3C32N8tBJC1Fv9aqbHiZV\nsjK9KpcvvC7wGZOZq25UNgUNDshe9OOlJcVUAUakePjS4jW52LyeIh0IqfetU9hj\njImVP6MFEGmpEfxvqdKn6skCgYAfjc1+KDUbqrKfst9YRi6Wy5nHvl/Y0NyFbpHC\nWeGumPYsjcc6tTilo4vviIIe2LE1kkR9sgEBNjLvkgILdWbUEkE8fYvPNNiKffiG\nka5GiTN5N12O9+qXmdLg0+5eQkh896zLZW1BYnLO9YEz8bfCjEwHJHyoatBVUFJS\nSVGyUQKBgC00Jo563DAqVrP7nv0VIvyTmr4m7TymX/8K8g+OY9uvJEGUpMndEoOn\nIRTFZeM5nTjg2sqkqt1PseFMkHzJEsVNNm6tZ/La3hpmn6f7b5fBjmItDoHYHxW0\nFMiug2+9P6RzDukbkeWQeEL0kQgd3UYuqgNZYmXV/h83zWJoHckw\n-----END RSA PRIVATE KEY-----\n","thingArn":"arn:aws:iot:us-east-1:235368163414:thing/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice","thingId":"83a64854-453d-4165-812b-20a97343f356","thingName":"AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice"}
INFO[0002] Saving config for '83a64854-453d-4165-812b-20a97343f356' into file 'AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice-83a64854-453d-4165-812b-20a97343f356.json'
You can also list your existing devices:
$ ./bin/db-device-list
INFO[0000] Using endpoint: https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev/ (in fact: https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev/device)
Response: [{"certificateId":"e768e1928dec019b3d748316572d17e2ae4531d21b6d126782976261799e1401","certificateArn":"arn:aws:iot:us-east-1:235368163414:cert/e768e1928dec019b3d748316572d17e2ae4531d21b6d126782976261799e1401","thingId":"83a64854-453d-4165-812b-20a97343f356","thingArn":"arn:aws:iot:us-east-1:235368163414:thing/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice","thingName":"myShinyDevice"}] (200)
[{"certificateId":"e768e1928dec019b3d748316572d17e2ae4531d21b6d126782976261799e1401","certificateArn":"arn:aws:iot:us-east-1:235368163414:cert/e768e1928dec019b3d748316572d17e2ae4531d21b6d126782976261799e1401","thingId":"83a64854-453d-4165-812b-20a97343f356","thingArn":"arn:aws:iot:us-east-1:235368163414:thing/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice","thingName":"myShinyDevice"}]
Ok, at this point, you already got a json file for connection. Take a break, soon we'll build a thermomether!
Building a ThermometerUnfortunately, gobot only allows working with devices it has a device driver - implemented from the client (go) side. So that limits our option. In our case, we'll resort to a BMP280 Thermometer. Using Adafruit's BMP280 shield, and using an I2C connection, it should be like this:
For more details, check https://learn.adafruit.com/adafruit-bmp280-barometric-pressure-plus-temperature-sensor-breakout/overview
Once the circuit has been made, open the Arduino IDE on your desktop, and under File | Examples | Firmata
, pick StandardFirmata
. Select your Board and Port and upload. Now attach the Arduino to your Raspberry Pi, and identify its serial port with the dmesg
command and checking under /dev/tty*
Using the json file we've generated in the previous section, fire up the thermometer reporter - the argument to the -s
option should be your serial port (in this case, /dev/ttyUSB0
):
./bin/db-bmp280-thermometer-service -s /dev/ttyUSB0 AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice-83a64854-453d-4165-812b-20a97343f356.json
INFO[0000] Loaded config file: AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice-83a64854-453d-4165-812b-20a97343f356.json for AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice @ a3slywy094gyae.iot.us-east-1.amazonaws.com
INFO[0003] Publishing state update to $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice/shadow/update (update: {"state":{"reported":{"Alexa.TemperatureSensor":{"3":{"temp":24.591446}}}}})
{
"_topic": "$aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice/shadow/update/accepted",
"metadata": {
"reported": {
"Alexa.TemperatureSensor": {
"3": {
"temp": {
"timestamp": 1518970228
}
}
}
}
},
"state": {
"reported": {
"Alexa.TemperatureSensor": {
"3": {
"temp": 24.591446
}
}
}
},
"timestamp": 1518970228,
"version": 1
}
INFO[0004] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice/shadow/update/accepted
INFO[0004] Connected
INFO[0004] Sending status update: {"state":{"reported":{"connected":true}}}
INFO[0004] Thing shadow updated successfully
{
"_topic": "$aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice/shadow/update/accepted",
"metadata": {
"reported": {
"connected": {
"timestamp": 1518970228
}
}
},
"state": {
"reported": {
"connected": true
}
},
"timestamp": 1518970228,
"version": 2
}
Keep it running. If things went ok and no errors, here's the steps you should take:
- Say "-Alexa, discover my devices"
- From the Alexa App, notice the new device will be there. Rename "myShinyDevice" to something easy for Alexa to understand such as 'Office'
- Say "-Alexa, whats the temperature at the Office"
- Enjoy!
Notice that at this point, adding new devices simply becomes easier!
But how does it work?Thanks for asking. For most of the scenarios on dasBridge, everything is held by means of the Thing Shadows API. As such, we publish to our Device Shadow a payload as this:
INFO[0304] Publishing state update to $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice/shadow/update (update: {"state":{"reported":{"Alexa.TemperatureSensor":{"3":{"temp":35.15287}}}}})
{
"_topic": "$aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myShinyDevice/shadow/update/accepted",
"metadata": {
"reported": {
"Alexa.TemperatureSensor": {
"3": {
"temp": {
"timestamp": 1518971390
}
}
}
}
},
"state": {
"reported": {
"Alexa.TemperatureSensor": {
"3": {
"temp": 35.15287
}
}
}
},
"timestamp": 1518971390,
"version": 14
}
Internally, the dasBridge Skill has a logic to parse the JSON Thing Descriptor and extract meaningful interfaces out of this:
discoverEndpoint(endpointId: string, thingMeta: ThingMetadata): AlexaDiscoveryEndpoint {
//console.log('arguments:', JSON.stringify(arguments, null, 2))
const friendlyNameToUSe = FULL_TO_SHORT.exec(endpointId)[1]
let result: AlexaDiscoveryEndpoint = {
endpointId: endpointId,
friendlyName: friendlyNameToUSe, // TODO
description: `${friendlyNameToUSe} Device`, // TODO
manufacturerName: "the dasbridge project",
capabilities: [],
displayCategories: []
}
let hasTemperatureTimestamp = jmespath.search(thingMeta, 'metadata.reported."Alexa.TemperatureSensor"."3".temp.timestamp')
if (hasTemperatureTimestamp && this.isLessThan30Minutes(hasTemperatureTimestamp)) {
result.capabilities.push({
type: "AlexaInterface",
interface: "Alexa.TemperatureSensor",
version: "3",
properties: {
supported: [
{name: "temperature"}
]
},
proactivelyReported: false,
retrievable: true
} as AlexaCapability)
result.displayCategories.push('TEMPERATURE_SENSOR')
}
// metadata.reported."Alexa.TemperatureSensor"."3".temp.timestamp
return result
}
This means: if there's an 'Alexa.TemperatureSensor
' key, with a sub key of '3
' and 'temp
', and its Thing Shadow Metadata has been reported under 30 minutes, it will report as a "TEMPERATURE_SENSOR
" interface.
Interacting with the Alexa Smart Home platform, when queried, becomes a matter of retrieving the Device Shadow and building its payload e.g.:
let hasTemperatureTimestamp = jmespath.search(thingMeta, 'metadata.reported."Alexa.TemperatureSensor"."3".temp.timestamp')
if (hasTemperatureTimestamp && this.isLessThan30Minutes(hasTemperatureTimestamp)) {
const sampleTime = new Date(hasTemperatureTimestamp * 1000)
const temperature = jmespath.search(thingMeta, 'state.reported."Alexa.TemperatureSensor"."3".temp')
const timeOfSample = moment(sampleTime).utc().toISOString()
answer.context.properties.push({
namespace: "Alexa.TemperatureSensor",
name: "temperature",
value: {
value: temperature.toFixed(1),
scale: "CELSIUS"
},
timeOfSample: timeOfSample,
uncertaintyInMilliseconds: 1000
})
}
Building a NeoPixel Strip LampNow lets try another example. Using an Arduino Uno and a Neopixel Strip, like this:
Now, open the Arduino IDE and install the Adafruit Neopixels Library. Once done, install the sketch on the client repo, under sketches/rgbmodem.ino, compile and upload. This sketch implements the AT interface, but only for managing the Leds.
You could compile and upload, and open the Serial Console. Try those commands to the Arduino:
- Set to green: AT+RGB=FF0000
- Turn it off: AT+OFF
Now reconnect it to the Raspberry Pi and generate a new device key for this lamp:
./bin/db-device-register myRGBLamp db-default-thing-type
INFO[0000] Using endpoint: https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev/ (in fact: https://t2p7v4dvcf.execute-api.us-east-1.amazonaws.com/dev/device)
Response: {"rootCertificates":{"https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem":"-----BEG...
OA_myRGBLamp","thingId":"5cdd7fb7-1216-4ea0-90df-9b49790288f0","thingName":"AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp"} (200)
INFO[0000] Response Content: {"rootCertificates":{"https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authori...
RSA PRIVATE KEY-----\n","thingArn":"arn:aws:iot:us-east-1:235368163414:thing/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp","thingId":"5cdd7fb7-1216-4ea0-90df-9b49790288f0","thingName":"AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp"}
INFO[0000] Saving config for '5cdd7fb7-1216-4ea0-90df-9b49790288f0' into file 'AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp-5cdd7fb7-1216-4ea0-90df-9b49790288f0.json'
And fire up its client:
./bin/db-rgb-service /dev/ttyUSB0 *myRGBLamp*
INFO[0000] Loaded config file: AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp-5cdd7fb7-1216-4ea0-90df-9b49790288f0.json for AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp @ a3slywy094gyae.iot.us-east-1.amazonaws.com
INFO[0000] Publishing state update to $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/update (update: {"state":{"reported":{"Alexa.ColorController":{"3":{"color":{"brightness":0,"hue":0,"saturation":0}}},"Alexa.PowerController":{"3":{"powerState":"OFF"}}}}})
ERRO[0000] Oops: runtime error: invalid memory address or nil pointer dereference
INFO[0000] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/update/accepted
INFO[0000] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/get
INFO[0000] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/get/rejected
INFO[0001] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/delete
INFO[0001] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/delete/accepted
INFO[0001] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/delete/rejected
INFO[0001] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/update
INFO[0001] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/update/documents
INFO[0001] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/update/rejected
INFO[0001] Subscribed to topic: $aws/things/AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp/shadow/get/accepted
INFO[0001] Connected
INFO[0001] Sending status update: {"state":{"reported":{"connected":true}}}
INFO[0001] Thing shadow updated successfully
This device implements two interfaces: Alexa.PowerController and Alexa.ColorController. Here's the flow to do:
- Say "-Alexa, discover my devices"
- Open the Alexa App and rename
myRGBLight
into something meaningful, such as "Desk Lamp"
- You an either operate from the Alexa App, or use Voice commands, such as "Alexa, turn [on|off] Desk Lamp" and/or "Alexa, set the Desk Lamp Color to [color]"
Perhaps you're more into videos, right? So here it is:
Packing UpOne neat trick is that you can actually ensure everything is loaded and configured comes from this URL. Basically, if you know your Arduino Configuration (USB Vendor / Product Id, as well as Serial Number), you can configure so everything can be started at boot time. So, for instance, I did look into the parameters for my Arduino on /dev/ttyACM2:
pi@rpi0:~ $ sudo udevadm info -a -n /dev/ttyACM2 | egrep -e '(serial|idVendor|idProduct)'
ATTRS{idProduct}=="0043"
ATTRS{idVendor}=="2341"
ATTRS{serial}=="7543831363335121C1F0"
ATTRS{idProduct}=="9514"
ATTRS{idVendor}=="0424"
ATTRS{idProduct}=="0002"
ATTRS{idVendor}=="1d6b"
ATTRS{serial}=="3f980000.usb"
I also created a systemd service mapping my configuration. It came like this:
$ cat /etc/systemd/system/rgblamp.service
[Unit]
Description=RGB Lamp Service
After=network.target
[Service]
WorkingDirectory=/home/pi/db-client
ExecStart=/home/pi/db-client/bin/db-rgb-service /dev/rgblamp AG6JLK4J4SVY6O2NZZIM5DLKVKOA_myRGBLamp-5cdd7fb7-1216-4ea0-90df-9b49790288f0.json
So in the end, I've got this configuration file under /dev/udev/rules.d/99-misc.rules:
SUBSYSTEM=="tty", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="0043", ATTRS{serial}=="7543831363335121C1F0", SYMLINK+="rgblamp", ENV{SYSTEMD_WANTS}
="rgblamp.service"
That meant that, if everything has been properly set, nothing was needed to connect my modules!
ConclusiondasBridge has a fun project to build. However, we've got lives and constraints on real life. If not, we'd be able to:
- Bring the vision to smaller hardware, specifically MCU's such as the ESP32, as well as some ST and/or nRF5x series
- Be able to come up with more samples, such as the smart camera
- Condier moving out of the Alexa Smart Home paradigm
However, those might be implemented in the future. Meanwhile, we invite you to check https://github.com/dasbridge and join the gitter rooms there.
Thank you!
Comments