Ejemplo n.º 1
0
def _matcher_for_topic(subscription: str) -> Any:
    # pylint: disable=import-outside-toplevel
    from paho.mqtt.matcher import MQTTMatcher

    matcher = MQTTMatcher()
    matcher[subscription] = True

    return lambda topic: next(matcher.iter_match(topic), False)
Ejemplo n.º 2
0
def _match_topic(subscription: str, topic: str) -> bool:
    """Test if topic matches subscription."""
    matcher = MQTTMatcher()
    matcher[subscription] = True
    try:
        next(matcher.iter_match(topic))
        return True
    except StopIteration:
        return False
Ejemplo n.º 3
0
    async def publish_wait(
        self,
        handler,
        messages: typing.Sequence[
            typing.Union[Message, typing.Tuple[Message, typing.Dict[str, typing.Any]]]
        ],
        message_types: typing.List[typing.Type[Message]],
        timeout_seconds: typing.Optional[float] = None,
    ):
        """Publish messages and wait for responses."""
        timeout_seconds = timeout_seconds or self.message_timeout_seconds

        # Start generator
        handler.send(None)

        handler_id = str(uuid4())
        handler_matcher = MQTTMatcher()

        self.handler_matchers[handler_id] = handler_matcher
        self.handler_queues[handler_id] = asyncio.Queue()

        # Subscribe to requested message types
        self.subscribe(*message_types)
        for message_type in message_types:
            # Register handler for each message topic
            handler_matcher[message_type.topic()] = handler

        # Publish messages
        for maybe_message in messages:
            if isinstance(maybe_message, Message):
                # Just a message
                self.publish(maybe_message)
            else:
                # Message and keyword arguments
                message, kwargs = maybe_message
                self.publish(message, **kwargs)

        try:
            # Wait for response or timeout
            result_awaitable = self.handler_queues[handler_id].get()

            if timeout_seconds > 0:
                # With timeout
                _, result = await asyncio.wait_for(
                    result_awaitable, timeout=timeout_seconds
                )
            else:
                # No timeout
                _, result = await result_awaitable

            yield result
        finally:
            # Remove queue
            self.handler_queues.pop(handler_id)
Ejemplo n.º 4
0
def _match_topic(subscription: str, topic: str) -> bool:
    """Test if topic matches subscription."""
    # pylint: disable=import-outside-toplevel
    from paho.mqtt.matcher import MQTTMatcher

    matcher = MQTTMatcher()
    matcher[subscription] = True
    try:
        next(matcher.iter_match(topic))
        return True
    except StopIteration:
        return False
def topic_matches_sub(sub, topic):
    """Check whether a topic matches a subscription.
    For example:
    foo/bar would match the subscription foo/# or +/bar
    non/matching would not match the subscription non/+/+
    """
    matcher = MQTTMatcher()
    matcher[sub] = True
    try:
        next(matcher.iter_match(topic))
        return True
    except StopIteration:
        return False
Ejemplo n.º 6
0
async def api_ws_mqtt(queue) -> None:
    """Websocket endpoint to send/receive MQTT messages."""
    topic_matcher = MQTTMatcher()
    receive_task = loop.create_task(websocket.receive())
    send_task = loop.create_task(queue.get())
    pending = {receive_task, send_task}

    while True:
        done, pending = await asyncio.wait(pending,
                                           return_when=asyncio.FIRST_COMPLETED)
        for task in done:
            if task is receive_task:
                try:
                    # Process received message
                    message = task.result()
                    handle_ws_mqtt(message, topic_matcher)
                except Exception:
                    _LOGGER.debug(message)
                    _LOGGER.exception("api_ws_mqtt (receive)")

                # Schedule another receive
                receive_task = loop.create_task(websocket.receive())
                pending.add(receive_task)
            elif task is send_task:
                try:
                    # Send out queued message (if matches topic)
                    message = task.result()
                    if message[0] == "mqtt":
                        topic, payload = message[1], message[2]
                        mqtt_message = json.dumps({
                            "topic":
                            topic,
                            "payload":
                            json.loads(payload)
                        })

                        for _ in topic_matcher.iter_match(topic):
                            await websocket.send(mqtt_message)
                            _LOGGER.debug("Sent %s char(s) to websocket",
                                          len(mqtt_message))
                            break
                except Exception:
                    _LOGGER.debug(topic)
                    _LOGGER.exception("api_ws_mqtt (send)")

                # Schedule another send
                send_task = loop.create_task(queue.get())
                pending.add(send_task)
Ejemplo n.º 7
0
    async def publish_wait(
        self,
        handler,
        messages: typing.List[typing.Union[Message, typing.Tuple[
            Message, typing.Dict[str, typing.Any]]]],
        topics: typing.List[str],
    ):
        """Publish messages and wait for responses."""
        # Start generator
        handler.send(None)

        handler_id = str(uuid4())
        handler_matcher = MQTTMatcher()

        self.handler_matchers[handler_id] = handler_matcher
        self.handler_queues[handler_id] = asyncio.Queue()

        # Subscribe to any outstanding topics
        for topic in topics:
            if topic not in self.topics:
                self.client.subscribe(topic)
                _LOGGER.debug("Subscribed to %s", topic)

            # Register handler for each message topic
            handler_matcher[topic] = handler

        # Publish messages
        for maybe_message in messages:
            if isinstance(maybe_message, Message):
                # Just a message
                self.publish(maybe_message)
            else:
                # Message and keyword arguments
                message, kwargs = maybe_message
                self.publish(message, **kwargs)

        # TODO: Add timeout
        done, result = await self.handler_queues[handler_id].get()
        print(done, result)
        yield result

        if done:
            # Remove queue
            self.handler_queues.pop(handler_id)
Ejemplo n.º 8
0
def acl(request):
    status_code = 200
    response_content = {}
    try:
        status_code = 403
        response_content = {"error": "Not authorized"}

        username = str(request.POST['username'])
        topic = str(request.POST['topic'])
        acc = int(request.POST['acc'])

        acls = MqttAcl.objects.filter(username=username)
        for acl in acls:
            matcher = MQTTMatcher()
            matcher[acl.topic] = True
            try:
                next(matcher.iter_match(topic))
                # ACC:
                # 1 = Read
                # 2 = Write
                # 4 - Ask to subscribe
                if acl.rw == acc or acl.rw == 3 or acc == 4:
                    status_code = 200
                    response_content = {"result": MqttAclSerializer(acl).data}
                    break
            except StopIteration:
                continue

    except MqttAccount.DoesNotExist as does_not_exist:
        status_code = 403
        response_content = {"error": "Account does not exist"}
    except MultiValueDictKeyError as e:
        status_code = 400
        response_content = {"error": "Field " + str(e) + " not sent"}
    except Exception as e:
        status_code = 500
        response_content = {"error": str(e)}

    response = JsonResponse(response_content)
    response.status_code = status_code
    return response
Ejemplo n.º 9
0
    def __init__(
        self,
        client,
        asr_url: typing.Optional[str] = None,
        asr_command: typing.Optional[typing.List[str]] = None,
        asr_train_url: typing.Optional[str] = None,
        asr_train_command: typing.Optional[typing.List[str]] = None,
        nlu_url: typing.Optional[str] = None,
        nlu_command: typing.Optional[typing.List[str]] = None,
        nlu_train_url: typing.Optional[str] = None,
        nlu_train_command: typing.Optional[typing.List[str]] = None,
        tts_url: typing.Optional[str] = None,
        wake_command: typing.Optional[typing.List[str]] = None,
        wake_sample_rate: int = 16000,
        wake_sample_width: int = 2,
        wake_channels: int = 1,
        handle_url: typing.Optional[str] = None,
        handle_command: typing.Optional[typing.List[str]] = None,
        word_transform: typing.Optional[typing.Callable[[str], str]] = None,
        certfile: typing.Optional[str] = None,
        keyfile: typing.Optional[str] = None,
        make_recorder: typing.Callable[[], VoiceCommandRecorder] = None,
        recorder_sample_rate: int = 16000,
        recorder_sample_width: int = 2,
        recorder_channels: int = 1,
        webhooks: typing.Optional[typing.Dict[str, typing.List[str]]] = None,
        skip_seconds: float = 0.0,
        min_seconds: float = 1.0,
        speech_seconds: float = 0.3,
        silence_seconds: float = 0.5,
        before_seconds: float = 0.5,
        vad_mode: int = 3,
        site_ids: typing.Optional[typing.List[str]] = None,
    ):
        super().__init__("rhasspyremote_http_hermes", client, site_ids=site_ids)

        # Speech to text
        self.asr_url = asr_url
        self.asr_command = asr_command
        self.asr_train_url = asr_train_url
        self.asr_train_command = asr_train_command
        self.asr_enabled = True
        self.asr_used = self.asr_url or self.asr_command
        self.asr_train_used = self.asr_train_url or self.asr_train_command
        self.asr_disabled_reasons: typing.Set[str] = set()

        # Intent recognition
        self.nlu_url = nlu_url
        self.nlu_command = nlu_command
        self.nlu_train_url = nlu_train_url
        self.nlu_train_command = nlu_train_command
        self.nlu_used = self.nlu_url or self.nlu_command
        self.nlu_train_used = self.nlu_train_url or self.nlu_train_command

        # Text to speech
        self.tts_url = tts_url
        self.tts_used = self.tts_url

        # Wake word detection
        self.wake_command = wake_command
        self.wake_enabled = True
        self.wake_proc: typing.Optional[subprocess.Popen] = None
        self.wake_sample_rate = wake_sample_rate
        self.wake_sample_width = wake_sample_width
        self.wake_channels = wake_channels
        self.wake_used = self.wake_command
        self.wake_disabled_reasons: typing.Set[str] = set()

        # Intent handling
        self.handle_url = handle_url
        self.handle_command = handle_command
        self.handle_enabled = True
        self.handle_used = self.handle_url or self.handle_command

        self.word_transform = word_transform

        # SSL
        self.ssl_context = ssl.SSLContext()
        if certfile:
            _LOGGER.debug("Using SSL with certfile=%s, keyfile=%s", certfile, keyfile)
            self.ssl_context.load_cert_chain(certfile, keyfile)

        # Async HTTP
        self._http_session: typing.Optional[aiohttp.ClientSession] = None

        # No timeout
        def default_recorder():
            return WebRtcVadRecorder(
                max_seconds=None,
                vad_mode=vad_mode,
                skip_seconds=skip_seconds,
                min_seconds=min_seconds,
                speech_seconds=speech_seconds,
                silence_seconds=silence_seconds,
                before_seconds=before_seconds,
            )

        self.make_recorder = make_recorder or default_recorder
        self.recorder_sample_rate = recorder_sample_rate
        self.recorder_sample_width = recorder_sample_width
        self.recorder_channels = recorder_channels

        # Webhooks
        self.webhook_matcher: typing.Optional[MQTTMatcher] = None
        self.webhook_topics: typing.List[str] = []

        if webhooks:
            self.webhook_matcher = MQTTMatcher()
            self.webhook_topics = list(webhooks.keys())
            for topic, urls in webhooks.items():
                for url in urls:
                    self.webhook_matcher[topic] = url

        # session_id -> AsrSession
        self.asr_sessions: typing.Dict[typing.Optional[str], AsrSession] = {}

        self.first_audio: bool = True

        # Start up
        if self.wake_command:
            self.start_wake_command()

        # Webhooks
        self.subscribe_topics(*self.webhook_topics)

        # Wake
        if self.wake_used:
            self.subscribe(HotwordToggleOn, HotwordToggleOff)

        # ASR
        if self.asr_used:
            self.subscribe(
                AsrStartListening,
                AsrStopListening,
                AsrToggleOn,
                AsrToggleOff,
                AudioFrame,
                AudioSessionFrame,
            )

        if self.asr_train_used:
            self.subscribe(AsrTrain)

        # NLU
        if self.nlu_used:
            self.subscribe(NluQuery)

        if self.nlu_train_used:
            self.subscribe(NluTrain)

        # TTS
        if self.tts_used:
            self.subscribe(TtsSay)

        # Intent Handling
        if self.handle_used:
            self.subscribe(NluIntent, HandleToggleOn, HandleToggleOff)