Search code examples
pythonjsonmqttiotaws-iot

problems with paho mqtt client's library implementation of an alexa skill to change the value of a topic of mosquitto broker


I'm a beginner with almost no experience developer of Alexa skills, I've been learning the basics and as a project I'd like to connect my Alexa skill to the mosquitto mqtt topic called test/1 and change the value of the topic whenever I want, I've already created the interaction model and built the skill but there's no response and Alexa just says that there's a problem while reaching my skill and basically doesn't work.

Btw I'm using the Alexa Developer Console, should I use aws lambda function? I'll leave the code here:

# -*- coding: utf-8 -*-
import logging
import ask_sdk_core.utils as ask_utils
#I really dont know for a fact that this is the proper way to connect to mosquitto
import paho.mqtt.client as mqtt
from ask_sdk_core.skill_builder import SkillBuilder
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.dispatch_components import AbstractExceptionHandler
from ask_sdk_core.handler_input import HandlerInput

from ask_sdk_model import Response

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


class LaunchRequestHandler(AbstractRequestHandler):
    """Handler for Skill Launch."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool

        return ask_utils.is_request_type("LaunchRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speak_output = "Bienvenido, con esta skill puedes modificar el estado del topico test/1 que se encuentra en el mqtt de Mosquitto"

        return (
            handler_input.response_builder
                .speak(speak_output)
                .ask(speak_output)
                .response
        )
class HelloWorldIntentHandler(AbstractRequestHandler):
    """Handler for Hello World Intent."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("HelloWorldIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speak_output = "Hello World!"

        return (
            handler_input.response_builder
                .speak(speak_output)
                # .ask("add a reprompt if you want to keep the session open for the user to respond")
                .response
        )

class Top1IntentHandler(AbstractRequestHandler):
    """Handler para el Intent del estado del topico 1."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("esTopicoUno")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        verb=handler_input.request_envolope.request.intent.slots['Verbo']
        verb=str(verb.value)
        estado = handler_input.request_envelope.request.intent.slots['estado']
        estado = int(estado.value)
         # Conectamos con el broker MQTT Mosquitto y publicamos el nuevo estado del tópico.
        client = mqtt.Client(client_id="mqtt-explorer-3985d2d2")
        client.connect("test.mosquitto.org", 1883)
        client.publish("test/1", estado)
        # Creamos la respuesta que Alexa devolverá al usuario.
        speak_output = f"El nuevo estado del tópico es {estado}."
        return (
            handler_input.response_builder
                .speak(speak_output)
                # .ask("add a reprompt if you want to keep the session open for the user to respond")
                .response
        )


class HelpIntentHandler(AbstractRequestHandler):
    """Handler for Help Intent."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("AMAZON.HelpIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speak_output = "puedo modificar el topico test/1, que te gustaria hacer?"

        return (
            handler_input.response_builder
                .speak(speak_output)
                .ask(speak_output)
                .response
        )


class CancelOrStopIntentHandler(AbstractRequestHandler):
    """Single handler for Cancel and Stop Intent."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return (ask_utils.is_intent_name("AMAZON.CancelIntent")(handler_input) or
                ask_utils.is_intent_name("AMAZON.StopIntent")(handler_input))

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        speak_output = "Hasta luego, gracias por usar la skill!"

        return (
            handler_input.response_builder
                .speak(speak_output)
                .response
        )

class FallbackIntentHandler(AbstractRequestHandler):
    """Single handler for Fallback Intent."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("AMAZON.FallbackIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        logger.info("In FallbackIntentHandler")
        speech = "Hmm, no estoy seguro que el valor que proporcionaste es correcto. Puedes decir Modifica el valor del topico al numero de estado que gustes o ayuda. ¿Qué te gustaría hacer?"
        reprompt = "No entendi, en que te puedo ayudar?"

        return handler_input.response_builder.speak(speech).ask(reprompt).response

class SessionEndedRequestHandler(AbstractRequestHandler):
    """Handler for Session End."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_request_type("SessionEndedRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response

        # Any cleanup logic goes here.

        return handler_input.response_builder.response


class IntentReflectorHandler(AbstractRequestHandler):
    """The intent reflector is used for interaction model testing and debugging.
    It will simply repeat the intent the user said. You can create custom handlers
    for your intents by defining them above, then also adding them to the request
    handler chain below.
    """
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_request_type("IntentRequest")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        intent_name = ask_utils.get_intent_name(handler_input)
        speak_output = "You just triggered " + intent_name + "."

        return (
            handler_input.response_builder
                .speak(speak_output)
                # .ask("add a reprompt if you want to keep the session open for the user to respond")
                .response
        )


class CatchAllExceptionHandler(AbstractExceptionHandler):
    """Generic error handling to capture any syntax or routing errors. If you receive an error
    stating the request handler chain is not found, you have not implemented a handler for
    the intent being invoked or included it in the skill builder below.
    """
    def can_handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> bool
        return True

    def handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> Response
        logger.error(exception, exc_info=True)

        speak_output = "Lo siento, tuve problemas para hacer lo que pediste. Por favor, inténtalo de nuevo."

        return (
            handler_input.response_builder
                .speak(speak_output)
                .ask(speak_output)
                .response
        )

# The SkillBuilder object acts as the entry point for your skill, routing all request and response
# payloads to the handlers above. Make sure any new handlers or interceptors you've
# defined are included below. The order matters - they're processed top to bottom.


sb = SkillBuilder()

sb.add_request_handler(LaunchRequestHandler())
sb.add_request_handler(HelloWorldIntentHandler())
sb.add_request_handler(HelpIntentHandler())
sb.add_request_handler(CancelOrStopIntentHandler())
sb.add_request_handler(FallbackIntentHandler())
sb.add_request_handler(SessionEndedRequestHandler())
sb.add_request_handler(IntentReflectorHandler()) # make sure IntentReflectorHandler is last so it doesn't override your custom intent handlers

sb.add_exception_handler(CatchAllExceptionHandler())

lambda_handler = sb.lambda_handler()

and the interaction model code is this:

{
    "interactionModel": {
        "languageModel": {
            "invocationName": "mqtt cidesi",
            "intents": [
                {
                    "name": "AMAZON.CancelIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.NavigateHomeIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.FallbackIntent",
                    "samples": []
                },
                {
                    "name": "esTopicoUno",
                    "slots": [
                        {
                            "name": "Verbo",
                            "type": "verbo"
                        },
                        {
                            "name": "estado",
                            "type": "AMAZON.NUMBER"
                        }
                    ],
                    "samples": [
                        "Por favor {Verbo} estado del topico a {estado}",
                        "Por favor {Verbo} el estado del topico por {estado}",
                        "{estado} sera el nuevo valor del topico",
                        "{Verbo} el nuevo estado del topico a {estado}",
                        "{Verbo} el nuevo valor del topico a {estado}",
                        "Haz que {estado} sea el  valor del topico",
                        "Haz que {estado} sea el nuevo valor del topico",
                        "{Verbo} el valor del topico a {estado}",
                        "{Verbo} el estado del topico a {estado}"
                    ]
                }
            ],
            "types": [
                {
                    "name": "verbo",
                    "values": [
                        {
                            "name": {
                                "value": "Mutua"
                            }
                        },
                        {
                            "name": {
                                "value": "Altera"
                            }
                        },
                        {
                            "name": {
                                "value": "Corrige"
                            }
                        },
                        {
                            "name": {
                                "value": "Rectifica"
                            }
                        },
                        {
                            "name": {
                                "value": "Ajusta"
                            }
                        },
                        {
                            "name": {
                                "value": "Transforma"
                            }
                        },
                        {
                            "name": {
                                "value": "Encargate "
                            }
                        },
                        {
                            "name": {
                                "value": "Actualiza"
                            }
                        },
                        {
                            "name": {
                                "value": "Modifica "
                            }
                        },
                        {
                            "name": {
                                "value": "Cambia"
                            }
                        }
                    ]
                }
            ]
        }
    }
}

sorry about the language but I'm a spanish native speaker.

I've tried to read about the uses of the IoT platform that AWS provides but there are too many barriers and there's like a lot to do before AWS and I'm feeling lost


Solution

  • Ok so no one else is talking about this functionality of aws so I'll elaborate a little bit, in order to establish a connection to pahoo mqtt (I believe that this needs to be done in order to use any other external library that is not included in the Alexa Skill Kit) you need to go into the requeriments.txt file and write

    paho-mqtt==1.6.1
    

    environment illustration

    then i corrected my code by splitting the code and parsing the value that I want to change in the topic to a string value. ill leave the code over here.

    class Top1IntentHandler(AbstractRequestHandler):
    def on_connect(self, client, userdata, flags, rc):
        print("Conexión establecida con éxito al broker MQTT")
    
    def on_message(self, client, userdata, message):
        print("Mensaje recibido en el tópico " + message.topic + " con el siguiente contenido: " + message.payload.decode())
    
    def on_disconnect(self, client, userdata, rc):
        print("Desconectado del broker MQTT")
    
    
    """Handler for Hello World Intent."""
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("esTopicoUno")(handler_input)
    
    def handle(self, handler_input):
        # Conf cliente MQTT
        num = handler_input.request_envelope.request.intent.slots['numeromqtt']
        num = str(num.value)
        client = mqtt.Client(client_id="my_client_dede")  # Asigna un identificador único para el cliente
        client.on_connect = self.on_connect  # Define la función callback que se ejecuta cuando se establece la conexión
        client.on_message = self.on_message  # Define la función callback que se ejecuta cuando se recibe un mensaje
        client.on_disconnect = self.on_disconnect  # Define la función callback que se ejecuta cuando se desconecta del broker
        client.connect("test.mosquitto.org", 1883)  # Conecta al broker Mosquitto
        client.publish("test/topic", f"{num}", 1, True)
        client.disconnect()  
        # type: (HandlerInput) -> Response
        speak_output = "mqtt test exitoso, se actualizo el valor!"
    
        return (
            handler_input.response_builder
                .speak(speak_output)
                # .ask("add a reprompt if you want to keep the session open for the user to respond")
                .response
        )