29

I would like to have an easy command like I use in the bash to publish something to a topic on MQTT inside a AWS Lambda function. Along the lines of: mosquitto_pub -h my.server.com -t "light/set" -m "on"

Background: I would like to turn a lamp on and off with Alexa. Alexa can start a Lambda function, and inside of this Lambda function I would like to start an MQTT publish, because the lamp can listen to a MQTT topic and react on the messages there.(Maybe there are easier solutions, but we are in a complicated (university) network which makes many other approaches more difficult)

Jedi
  • 3,088
  • 2
  • 28
  • 47
matt_55_55
  • 291
  • 1
  • 3
  • 5

7 Answers7

48

If you are using Python, I was able to get an AWS Lambda function to publish a message to AWS IoT using the following inside my handler function:

import boto3
import json

client = boto3.client('iot-data', region_name='us-east-1')

# Change topic, qos and payload
response = client.publish(
        topic='$aws/things/pi/shadow/update',
        qos=1,
        payload=json.dumps({"foo":"bar"})
    )

You will also need to ensure that the Role (in your Lambda function configuration) has a policy attached to allow access to IoT publish function. Under IAM -> Roles you can add an inline policy to your Lambda function Role like:

{
   "Version": "2016-6-25",
   "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "iot:Publish"
        ],
        "Resource": [
            "*"
        ]
    }
   ]
}
Aurora0001
  • 13,139
  • 5
  • 50
  • 53
Roy
  • 588
  • 4
  • 10
  • Thanks for your help Roy. In the end we used Ponte, got out of the AWS lambda with an http request that was sent to ponte which then transferred it to mqtt and sent that to the lamp. – matt_55_55 Jun 29 '16 at 11:53
  • @xtra that was my question also, in JavaScript it is annoying that we need the endpoint, while in Python not. Have you figured out how to do that in JS without passing the endpoint? – Mohammed Noureldin Aug 01 '21 at 15:23
13

If you're using Node.js, this will work -

var AWS = require('aws-sdk');
var iotdata = new AWS.IotData({ endpoint: '*****************.iot.us-east-1.amazonaws.com' });

exports.handler = async(event) => {
  console.log("Event => " + JSON.stringify(event));
  var params = {
      topic: "MyTopic",
      payload: JSON.stringify(event),
      qos: 0
  };
    
  return iotdata.publish(params, function(err, data) {
      if (err) {
          console.log("ERROR => " + JSON.stringify(err));
      } else {
          console.log("Success");
      }
  }).promise();
};

Remember to add iot:publish permission to the role used by this lambda function.

victorbalssa
  • 2,762
  • 2
  • 13
  • 25
Chirag Mittal
  • 1,508
  • 1
  • 12
  • 17
  • Why do we need to specify the endpoint in JS, but in python we can simply only pass the region? – Mohammed Noureldin Aug 01 '21 at 15:21
  • @MohammedNoureldin we can have multiple endpoints in the same region. – Chirag Mittal Aug 02 '21 at 08:11
  • Oh, I did not know that, I need to check how is this possible. Or could you please show how could this be possible? Or what is the key-word to search for? because I could not find this in a quick search. But apparently Python boto3 is possible to get the "default" endpoint without specifying it? – Mohammed Noureldin Aug 02 '21 at 08:16
  • @MohammedNoureldin I don't have any experience with Python boto3, so won't be able to help with that. – Chirag Mittal Aug 09 '21 at 13:10
8

The AWS SDK has two classes to work with IoT: Iot and IotData. IotData.publish is the method you are looking for. It looks like the Iot object is for working with things and IotData is for working with MQTT and shadows. This ought to be directly referenced in the documentation on MQTT and shadows, but it isn't.

This service (IotData) is also available in the CLI.

dpurrington
  • 1,488
  • 11
  • 20
  • 1
    Thanks for this answer. It got me on the right track with publishing an event from bash: `aws iot-data publish --cli-input-json '{"topic":"myTopic", "qos": 0, "payload": "{\"myKey\": 54321, \"myOtherKey\": 12345"}'`. Payload's keys have to be in double quotes and escaped with backslashes. – Perttu Haliseva Jan 15 '18 at 12:24
3

The previous post about nodeJS send the message 2 times for me. Correction is here

    var mqttParams = {
        topic: topicName,
        payload: JSON.stringify(event),
        qos: 1
    };
    
    const request = iotdata.publish(mqttParams);
    request
        .on('success', () => console.log("Success"))
        .on('error', () => console.log("Error"))
    return new Promise(() => request.send());
xmoulin
  • 31
  • 3
1

This worked for me using Rust.

main.rs:

use lambda_http::aws_lambda_events::serde_json;
use lambda_runtime::{service_fn, Error, LambdaEvent};
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<(), Error> {
    let func = service_fn(func);
    lambda_runtime::run(func).await?;
    Ok(())
}

async fn func(_event: LambdaEvent<Value>) -> Result<(), Error> {
    let config = aws_config::load_from_env().await;
    let client = aws_sdk_iotdataplane::Client::new(&config);
    let publish = client
        .publish()
        .topic("topic")
        .qos(1)
        .payload(aws_smithy_types::Blob::new("payload"));
    publish.send().await?;
    Ok(())
}

Cargo.toml:

[package]
name = "MyLambda"
version = "0.1.0"
edition = "2021"

[dependencies]
lambda_http = { version = "0.7", default-features = false, features = ["apigw_http"] }
lambda_runtime = "0.7"
tokio = { version = "1", features = ["macros"] }
aws-config = "0.51.0"
aws-sdk-iotdataplane = "0.21.0"
aws-smithy-types = "0.51.0"

rherrmannr
  • 88
  • 6
0

If you use Node.js, you need to install the mqtt library. The following steps help you download and install mqtt library on AWS Lambda.

  1. Download and install Node.js and npm on your PC.

  2. Download MQTT library for node.js.

  3. Unzip it at the nodejs directory that Node.js was installed. (In Windows 10 x64, nodejs directory is C:\Program Files\nodejs)

  4. Create a folder to store the mqtt installed files. For example, D:\lambda_function.

  5. Run Command Prompt as administrator, change directory to nodejs directory.

  6. Install mqtt library to D:\lambda_function.

    C:\Program Files\nodejs>npm install --prefix "D:\lambda_function” mqtt 
    

Here's a similar project.

Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
0

Here is a simple JavaScript code using async await:

const AWS = require("aws-sdk");

exports.handler = async event => {
  try {
    const iotData = new AWS.IotData({ endpoint: "IOT_SERVICE_ID-ats.iot.eu-central-1.amazonaws.com" });

    const params = {
      topic: 'some/topic',
      payload: JSON.stringify({ var1: "val1" })
    }

    result = await iotData.publish(params).promise();

    console.log(result);

    return { statusCode: 200, body: `IoT message published.` };
  } catch (e) {
    console.error(e);
    return { statusCode: 500, body: `IoT message could not be published.` };
  }
};

Don't forget to give this lambda the required iot:publish permission to publish to this IoT topic.

Mohammed Noureldin
  • 14,913
  • 17
  • 70
  • 99