1

I'm totally new to nodejs coming from a vb.net background. I've looked at so many examples of using callbacks but I just don't get it. So sorry if this is very basic.

I'm writing a AWS Lambda function and trying to get a value from a AWS IOT Thing Shadow and assign it to a variable. But I can't get it to work, however the log does show that the value was returned. I know it has to do with sync/async, but just lost the plot to how to get it. Here is the code so far:

//determine doorstate
function getdoorstate() { 
    //ask the thing
    var currentstate;
        var paramsGet = {
        "thingName": "garagedoor1",
        };
    iotData.getThingShadow(paramsGet, function (err, data) {
        if (err) {
        console.log("Error : " + err, err.stack);
          } else {
    console.log(data.payload);
    var obj = JSON.parse(data.payload);
    currentstate=obj["state"]["reported"]["doorstate"];
    console.log("The function doorstate is: "+currentstate);
            }
       });
    }

var doorstate = getdoorstate();

The log shows the console writes fine (presume they happen after the data has been retrieved):

INFO    {"state":{"desired":{"doorstate":0,"transitstate":0},"reported":{"doorstate":0,"transitstate":0}},"metadata":{"desired":{"doorstate":{"timestamp":1591241517},"transitstate":{"timestamp":1591241517}},"reported":{"doorstate":{"timestamp":1591241517},"transitstate":{"timestamp":1591241517}}},"version":444,"timestamp":1591241860}
The function doorstate is: 0

However the value returned is: undefined

Can anyone suggest how to change my code to use callback properly. Make it simple - I'm new! Thanks!


UPDATED CODE AS SUGGESTED BY Sagar - STILL ISSUE

//determine doorstate
function getdoorstate() { 
    //ask the thing
        var paramsGet = {
        "thingName": "garagedoor1",
        };
    iotData.getThingShadow(paramsGet, function (err, data) {
        if (err) {
        console.log("Error : " + err, err.stack);
          } else {
            callback(data)
            }
       });
    }
 getdoorstate(function (value){
     console.log(value);
 });
var doorstate = getdoorstate();

What am I doing wrong again?

2 Answers 2

1

getdoorState function has async operation if you call it directly process for fetching iot will be in the background and it will start executing the next line.

so When you call directly getdoorstate() it will always return undefined. You need to wait for api call to returm the data using callback, promises or async await

You can try something like this

// using call back
function getdoorstate(callback) { 
    //ask the thing

    iotData.getThingShadow(paramsGet, function (err, data) {
        if (err) {
        console.log("Error : " + err, err.stack);
          } else {
            callback(data)
            }
       });
    }

 getdoorstate(function (value){
     console.log(value)
     // Access value here 
 });
 // Below line won't work
 var value = getdoorstate()



// using promises
 function getdoorstate(callback) { 
    return new Promise((resolve, reject) => {
        //ask the thing

        iotData.getThingShadow(paramsGet, function (err, data) {
            if (err) 
                reject(err)
            else 
                resolve(data)
        });
        }
    })

 getdoorstate()
 .then(data => {
    console.log(data)
 })
 .catch(err=>{
     console.log(data)
 })
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks - pasted it but still have the issue - probably some newbie mistake. Also complains about "callback not defined". This is what I have now:
See edited code in original post - sorry if that is the wrong place.
function getdoorstate(callback) { We need to define callback here
also var doorstate = getdoorstate(); this won't work. we can only access data inside call back. So in our case getdoorstate(function (value){ inside this
Sorry - system says I can't: Votes cast by those with less than 15 reputation are recorded, but do not change the publicly displayed post score.
|
0

Based on the above help from Sagar, I've now been able to get my head around callbacks, promises and await in nodejs. (Well I think so anyway.) The code snippet below is now a working example extracted from my Lambda function. The rest of the code is just based on samples available on Github or AWS.

My solution was to use a callback for one part and async/promise/await for the code in the Alexa handler code. This probably could now be reduced to something simpler so the code is not repeated in each handler, but for what it is worth, I hope this code below helps some other newbie to nodejs and AWS IOT/Alexa to work out how to get the Lamda to wait to get the values needed from the IOT thing shadow.

// more code above ...(see ASK and AWS samples)

//IOT Stuff - settings
AWS.config.region = "<put your own region in here>";
var IOT_Data = new AWS.IotData({
    endpoint: "<put your IOT Shadow Endpoint in here>"
});
var IOT_Thing = {
    "thingName": "<put the name of your thing here>"
};
var doorstate; //this is used to hold the part of JSON data I want 

//additional functions
//get shadow of thing using a call back.  this function uses a nodejs callback 
function getdoorstate(callback) {
    IOT_Data.getThingShadow(IOT_Thing, function(err, data) {
        if (err) {
            console.log("Error : " + err, err.stack);
        } else {
            callback(data);
        }
    });
}

//..more code in here.. not shown in this snippet

//this is one of the Alexa intent handlers

// core functionality
const DoorStateHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        // checks request type - this one handes an intent with name of doorState
        return request.type === 'IntentRequest' &&
            request.intent.name === 'doorState';
    },
// important to use the async here to get the code to wait for the 'promise' (uses nodejs promise and await)
    async handle(handlerInput) {
        var speakOutput = "Let me check..."; /set a var ready for the speech to send back to Alexa
        //set up a promise so that we can get the value I need from the thing shadow
    let promise = new Promise((resolve, reject) => {
            getdoorstate(function(value) {
                var obj = JSON.parse(value.payload); //parse to json
                doorstate = obj["state"]["reported"]["doorstate"]; //return just the value of the doorstate
                console.log("The doorstate is: " + doorstate);
                speakOutput = speakOutput.concat(msg[doorstate]); //concatenate this to the var containing the speech
                resolve(speakOutput); //resolve the promise and return the speech
            });
        });
        //return result
    // this is important - use the await here so that we dont proceed any further until the promise is resolved with the thing values
        let result = await promise; 
        return handlerInput.responseBuilder
            .speak(result)
            .withSimpleCard("Garage Door", result)
            .getResponse();
    },
};

// this is another handler for sending a desired action to the IOT shadow 
const DoorTransitHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        // checks request type
        return request.type === 'IntentRequest' &&
            request.intent.name === 'activate_door';
    },
    //again use async so that we can use promise and await - all the rest like above..
    async handle(handlerInput) {
        var speakOutput = "I'm on it...";
        let promise = new Promise((resolve, reject) => {
            getdoorstate(function(value) {
                var obj = JSON.parse(value.payload);
                doorstate = obj["state"]["reported"]["doorstate"];
                console.log("The doorstate is: " + doorstate);
                speakOutput = speakOutput.concat(msg[doorstate]);
                resolve(speakOutput);
            });
        });
        //return result
        let result = await promise;
        // set output
        result += "I'll push the button for you. ";

    //build the json to send back to the IOT shadow to get the thing to do something
        var paramsUpdate = {
            "topic": "$aws/things/garagedoor1/shadow/update",
            "payload": '{"state":{"desired":{"doorstate":0,"transitstate":1}}}',
            "qos": 0
        };
    //now here we send our desired action back to the IOT Thing Shadow
        IOT_Data.publish(paramsUpdate, function(err, data) {
            if (err) console.log(err, err.stack); // an error occurred
            else console.log(data.payload); // successful response
        });

        // return response by means of Alexa
        return handlerInput.responseBuilder
            .speak(result)
            .getResponse();
    },
};

// and the code continues...  (see ASK and AWS samples)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.