7

Newbie question. Can't find a previous answer.

I want to build a simple pump controller with Alexa. Would like Alexa to report pump state.

Simplest approach is Alexa -> lambda -> publish_to_Iot. And then, or perhaps just before the publish, subscribe to another topic to which the local controller would publish pump state that would be passed back thru Alexa.

As near as I can tell its not possible to subscribe to a topic from Lambda... which actually makes sense in the context of a lambda function.

Specific question is, can a lambda function subscribe to an IoT topic?

Yes, I know about IoT shadows, was hoping to avoid some complexity.

big
  • 71
  • 1
  • 4
  • This question is not suitable for StackOverFlow, please read how to write a good questuin in SOF. You have to show us what you have tried and where is your problem exactly. – Mohammed Noureldin Dec 31 '17 at 18:20
  • I updated with a specific question. But I haven't tried anything yet. Was hoping to get some advice before I started. I guess that's against the rules. – big Dec 31 '17 at 18:26

3 Answers3

9

It's actually possible to do this, it's just not terribly intuitive. The AWS-SDK doesn't have a subscribe method so you need to use aws-iot-device-sdk. This library typically needs a certificate and lot of other config information.

var device = awsIot.jobs({
  keyPath: "./42fc9544f6-private.pem.key",
  certPath: "./42fc9544f6-certificate.pem.crt",
  caPath: "./AmazonRootCA1.pem",
  clientId: "clientPolicy", // name of the policy
  host: "your-endpoint.iot.us-east-1.amazonaws.com"
});

but it doesn't make sense to use a client certificate in a lambda. The lambda is already running under an IAM user so you should just be able to leverage that right? It turns out you can but it took a little digging. The aws-iot-device-sdk.js library will read the credentials out of the environment variables in Lambda (process.env.AWS_ACCESS_KEY_ID, process.env.AWS_SECRET_ACCESS_KEY). But you HAVE to use the wss protocol.

var awsIot = require('aws-iot-device-sdk');

device = awsIot.jobs({
  host: 'a1jcq6m7bg94jb-ats.iot.us-east-1.amazonaws.com',
  protocol: 'wss'});

device
  .on('connect', function() {
    console.log('connected. subscribing to topic...');
    device.subscribe(topic);
    console.log('subscribed to topic');   
});

One of the pitfalls of this approach is that there is natural latency in Lambda and of course additional latency to establish a connection to the topic. This really slows things down. A nice pattern to follow is to have lambda listen on a topic that is specific to that Lambda instance (e.g. lambda/some-uuid/response) and then when you post a message to your device, you can ask it to respond on that topic. The benefit is that the topic lives as long as the Lambda function is up and running. That could be hours if there is a lot of traffic or if you keep it warm. With this model, there is no latency to establish the connection and subscribe to the topic. In my tests, this is extremely fast with low latency.

This is how I handled the subscription.

var subscriber = null;

const lambdaUUID = uuidv4();
const topic = 'lambda/' + lambdaUUID + '/response';

device.on('message', function(topic, payload) {
  console.log('incoming message on topic' + topic);
  if ( subscriber ) {
    console.log('calling subscriber');
    subscriber(topic, payload);
  } else {
    console.log("no subscriber");
  }
});


exports.handler =  async function(event, context) {
  console.log("EVENT: \n" + JSON.stringify(event, null, 2));

  var deviceRequest = {"some":"stuff", callback: topic};

  const promise = new Promise(function(resolve, reject) {
    subscriber = function(topic, payload ) {
      console.log('subscriber called, resolving promise...');
      resolve("SUCCESS " + payload);
    };
  });  

  device.publish('things/THING1234/incoming', JSON.stringify(deviceRequest,null,''), { qos:0 }, function(err, data) {
    console.log("publishing message to device",err,data);
  });

  return promise;
};
Michael Connor
  • 4,182
  • 24
  • 21
1

You can trigger Lambda functions in response to a matching filter using rules (the filter will match the topic).

Aaron D
  • 31
  • 1
  • 3
  • Thanks for the comment, but I don't think that works. – big Jan 01 '18 at 19:18
  • Sorry, still learning how to use stack-overflow... As far as I can tell, the only way to wake up Alexa is via the wake word. You cannot wake up Alexa via some random lambda function. The whole thing needs to run in the context of the Alexa initiated lambda function, something like: Alexa wake word -> lambda -> iot-publish -> iot-subscribe -> (same) lambda -> return to Alexa – big Jan 01 '18 at 19:26
  • Oh, you're looking to have the triggered function publish, subscribe, and receive from an IoT topic? I thought you were looking for Alexa -> lambda -> publish -> lambda. This probably isn't a good fit for IoT. You might look at DynamoDB or SQS to pass data back and forth. You could subscribe a lambda function to topics but that's going to be way more fragile than needed, especially at scale. – Aaron D Jan 02 '18 at 01:46
0

The answer you are looking for is sending the notification from the Device to Alexa. Example: "Hi <<{User}>>, Motor pump <<{nameofpump}>> is Running" based on the actual status of the pump. This is more like a notification feature, please check this link on Alexa Voice Service which would help you to do the same. Hope this helps.

Siva Karthikeyan
  • 544
  • 8
  • 25