I am trying to get my Alexa skill to publish to an MQTT Broker in the lambda code. However it only publishes sporadically. Could somebody tell me what I am missing? Here is my code:
const Alexa = require('ask-sdk-core');
const mqtt = require("mqtt");
const protocol = 'mqtt'
const host = 'm21.cloudmqtt.com'
const port = '14297'
const clientId = `mqtt_${Math.random().toString(16).slice(3)}`
const connectUrl = `${protocol}://${host}:${port}`
console.log(connectUrl);
const client = mqtt.connect(connectUrl, {
clientId,
clean: false,
connectTimeout: 4000,
username: '****',
password: '***',
reconnectPeriod: 1000,
})
client.on('connect', function(connack) {
console.log("MQTT Client connected " + JSON.stringify(connack));
// publish a message to a topic
client.publish('/IoTmanager/YardESP/Garage_close3/control', '{"status":0}', function() {
console.log("Message is published");
client.end(); // Close the connection when published
});
});
client.on('end', () => {
console.log('Connection to MQTT broker ended');
});
//
const LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
const speechText = 'Would you like to open ur shut the Garage Door?';
console.log("MQTT Connecting");
/*client.on("connect", () => {
console.log("MQTT Connected");
client.publish("presence", "Hello mqtt");
});*/
//
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.withSimpleCard('Would you like to open ur shut the Garage Door?', speechText)
.getResponse();
}
};
const GarageDoorCloseIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'GarageDoorCloseIntent';
},
handle(handlerInput) {
const speechText = 'Closing The garage door!';
console.log("MQTT Options: ", speechText);
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Closing The garage door!', speechText)
.getResponse();
}
};
const GarageDoorOpenIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'GarageDoorOpenIntent';
},
handle(handlerInput) {
const speechText = 'Opening The garage door!';
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Opening The garage door!', speechText)
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speechText = 'You can ask me the weather!';
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.withSimpleCard('You can ask me the weather!', speechText)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
&& (Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent'
|| Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speechText = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Goodbye!', speechText)
.withShouldEndSession(true)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
},
handle(handlerInput) {
// Any clean-up logic goes here.
return handlerInput.responseBuilder.getResponse();
}
};
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`Error handled: ${error.message}`);
return handlerInput.responseBuilder
.speak('Sorry, I don\'t understand your command. Please say it again.')
.reprompt('Sorry, I don\'t understand your command. Please say it again.')
.getResponse();
}
};
let skill;
exports.handler = async function (event, context) {
console.log(`REQUEST++++${JSON.stringify(event)}`);
console.log(context);
if (!skill) {
skill = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
GarageDoorCloseIntentHandler,
GarageDoorOpenIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
)
.addErrorHandlers(ErrorHandler)
.create();
}
const response = await skill.invoke(event, context);
console.log(`RESPONSE++++${JSON.stringify(response)}`);
return response;
};
I try invoking the Alexa skill and I get a proper response from/to Alexa but only sporadic messages publishing to the MQTT Broker.
Ok I have resolved my issue thanks to these two answers. How to use async functions within Alexa skill properly? and https://github.com/mqttjs/async-mqtt. It looks like the MQTT has to be wrapped in a async funtion a bit like this:
const Alexa = require('ask-sdk-core');
const mqtt = require("mqtt");
var options = {
port: 14297,
username: "****",
password: "***",
connectTimeout: 3000,
debug: true
};
var client;
LaunchRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
},
handle(handlerInput) {
const speechText = 'Would you like to open ur shut the Garage Door?';
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.withSimpleCard('Would you like to open ur shut the Garage Door?', speechText)
.getResponse();
}
};
const GarageDoorCloseIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest' &&
Alexa.getIntentName(handlerInput.requestEnvelope) === 'GarageDoorCloseIntent';
},
async handle(handlerInput) {
const speechText = 'Closing the garage door!';
client = await mqtt.connectAsync('mqtt://m21.cloudmqtt.com', options);
try {
await client.publish("wow/so/cool", "It works!");
// This line doesn't run until the server responds to the publish
await client.end();
// This line doesn't run until the client has disconnected without error
console.log("Done");
} catch (e) {
// Do something about it!
console.log(e.stack);
process.exit();
}
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Closing the garage door!', speechText)
.getResponse();
}
};
const GarageDoorOpenIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest' &&
Alexa.getIntentName(handlerInput.requestEnvelope) === 'GarageDoorOpenIntent';
},
async handle(handlerInput) {
const speechText = 'Opening the garage door!';
client = await mqtt.connectAsync('mqtt://m21.cloudmqtt.com', options);
try {
await client.publish("wow/so/cool", "It works!");
// This line doesn't run until the server responds to the publish
await client.end();
// This line doesn't run until the client has disconnected without error
console.log("Done");
} catch (e) {
// Do something about it!
console.log(e.stack);
process.exit();
}
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Opening The garage door!', speechText)
.getResponse();
}
};
const HelpIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest' &&
Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.HelpIntent';
},
handle(handlerInput) {
const speechText = 'You can ask me to open or shut garage door!';
return handlerInput.responseBuilder
.speak(speechText)
.reprompt(speechText)
.withSimpleCard('You can ask me to open or shut garage door!', speechText)
.getResponse();
}
};
const CancelAndStopIntentHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest' &&
(Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.CancelIntent' ||
Alexa.getIntentName(handlerInput.requestEnvelope) === 'AMAZON.StopIntent');
},
handle(handlerInput) {
const speechText = 'Goodbye!';
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Goodbye!', speechText)
.withShouldEndSession(true)
.getResponse();
}
};
const SessionEndedRequestHandler = {
canHandle(handlerInput) {
return Alexa.getRequestType(handlerInput.requestEnvelope) === 'SessionEndedRequest';
},
async handle(handlerInput) {
// Any clean-up logic goes here.
client = await mqtt.connectAsync('mqtt://m21.cloudmqtt.com', options);
try {
await client.publish("wow/so/cool", "It works!");
// This line doesn't run until the server responds to the publish
await client.end();
// This line doesn't run until the client has disconnected without error
console.log("Done");
} catch (e) {
// Do something about it!
console.log(e.stack);
process.exit();
}
return handlerInput.responseBuilder.getResponse();
}
};
const ErrorHandler = {
canHandle() {
return true;
},
handle(handlerInput, error) {
console.log(`Error handled: ${error.message}`);
return handlerInput.responseBuilder
.speak('Sorry, I don\'t understand your command. Please say it again.')
.reprompt('Sorry, I don\'t understand your command. Please say it again.')
.getResponse();
}
};
let skill;
exports.handler = async function(event, context) {
console.log(`REQUEST++++${JSON.stringify(event)}`);
if (!skill) {
skill = Alexa.SkillBuilders.custom()
.addRequestHandlers(
LaunchRequestHandler,
GarageDoorCloseIntentHandler,
GarageDoorOpenIntentHandler,
HelpIntentHandler,
CancelAndStopIntentHandler,
SessionEndedRequestHandler,
)
.addErrorHandlers(ErrorHandler)
.create();
}
const response = await skill.invoke(event, context);
console.log(`RESPONSE++++${JSON.stringify(response)}`);
return response;
};