Usage

The design philosophy of Rhasspy Hermes App is:

  • It should be easy to create a Rhasspy app with minimal boilerplate code.

  • All Rhasspy Hermes App code should behave by default in a sensible way.

Reacting to an intent

This example app reacts to the default “GetTime” intent that comes with Rhasspy’s installation by telling you the current time:

"""Example app to react to an intent to tell you the time."""
import logging
from datetime import datetime

from rhasspyhermes.nlu import NluIntent

from rhasspyhermes_app import EndSession, HermesApp

_LOGGER = logging.getLogger("TimeApp")

app = HermesApp("TimeApp")


@app.on_intent("GetTime")
async def get_time(intent: NluIntent):
    """Tell the time."""
    now = datetime.now().strftime("%H %M")
    return EndSession(f"It's {now}")


app.run()

Ignoring the import lines and the logger, what this code does is:

  • creating a rhasspyhermes_app.HermesApp object;

  • defining an async function get_time that ends a session by telling the time;

  • running the app.

By applying the app’s rhasspyhermes_app.HermesApp.on_intent() decorator to the function, this function will be called whenever the app receives an intent with the name “GetTime”.

Try the example app time_app.py with the --help flag to see what settings you can use to start the app (mostly connection settings for the MQTT broker):

$ python3 examples/time_app.py --help
usage: TimeApp [-h] [--host HOST] [--port PORT] [--username USERNAME]
               [--password PASSWORD] [--tls] [--tls-ca-certs TLS_CA_CERTS]
               [--tls-certfile TLS_CERTFILE] [--tls-keyfile TLS_KEYFILE]
               [--tls-cert-reqs {CERT_REQUIRED,CERT_OPTIONAL,CERT_NONE}]
               [--tls-version TLS_VERSION] [--tls-ciphers TLS_CIPHERS]
               [--site-id SITE_ID] [--debug] [--log-format LOG_FORMAT]

optional arguments:
  -h, --help            show this help message and exit
  --host HOST           MQTT host (default: localhost)
  --port PORT           MQTT port (default: 1883)
  --username USERNAME   MQTT username
  --password PASSWORD   MQTT password
  --tls                 Enable MQTT TLS
  --tls-ca-certs TLS_CA_CERTS
                        MQTT TLS Certificate Authority certificate files
  --tls-certfile TLS_CERTFILE
                        MQTT TLS client certificate file (PEM)
  --tls-keyfile TLS_KEYFILE
                        MQTT TLS client key file (PEM)
  --tls-cert-reqs {CERT_REQUIRED,CERT_OPTIONAL,CERT_NONE}
                        MQTT TLS certificate requirements for broker (default:
                        CERT_REQUIRED)
  --tls-version TLS_VERSION
                        MQTT TLS version (default: highest)
  --tls-ciphers TLS_CIPHERS
                        MQTT TLS ciphers to use
  --site-id SITE_ID     Hermes site id(s) to listen for (default: all)
  --debug               Print DEBUG messages to the console
  --log-format LOG_FORMAT
                        Python logger format

You can pass all the settings as keyword arguments inside the constructor as well: rhasspyhermes_app.HermesApp("ExampleApp", host="192.168.178.123", port=12183). Note that arguments passed on the command line have precedence over arguments passed to the constructor.

Connecting to Rhasspy

In its default configuration, Rhasspy’s internal MQTT broker listens on port 12183, so this is what you need to connect to, using command-line or constructor arguments - see previous section for details.

If you are using docker, you will need to add to add this port to your docker-compose.yml file:

services:
  rhasspy:
    ports:
      - "12101:12101"   # this is the port used for the web interface
      - "12183:12183"   # you need this to access Rhasspy's MQTT port

If you’re using an external MQTT broker, you probably want port 1883. This is what most MQTT brokers use, and is Rhasspy Hermes App’s default port.

Continuing a session

An intent handler doesn’t have to end a session. You can continue the session to ask the user for extra information or just for a confirmation. For example:

"""Example app that shows how to continue a session."""
import logging

from rhasspyhermes.nlu import NluIntent

from rhasspyhermes_app import ContinueSession, EndSession, HermesApp

_LOGGER = logging.getLogger("ContinueApp")

app = HermesApp("ContinueApp")


@app.on_intent("Yes")
async def yes(intent: NluIntent):
    """The user confirms."""
    if intent.custom_data == "TurnOffLight":
        response = "OK, turning off the light"
    elif intent.custom_data == "TurnOnLight":
        response = "OK, turning on the light"
    else:
        response = "We can!"

    return EndSession(response)


@app.on_intent("No")
async def no(intent: NluIntent):
    """The user says no."""
    return EndSession()


@app.on_intent("TurnOffLight")
async def turn_off_light(intent: NluIntent):
    """The user asks to turn off the light."""
    return ContinueSession(
        text="Do you really want to turn off the light?", custom_data="TurnOffLight"
    )


@app.on_intent("TurnOnLight")
async def turn_on_light(intent: NluIntent):
    """The user asks to turn on the light."""
    return ContinueSession(
        text="Do you really want to turn on the light?", custom_data="TurnOnLight"
    )


app.run()

The functions turn_off_light and turn_on_light triggered by the intents TurnOffLight and TurnOnLight, respectively, each continue the current session by asking for confirmation. They set the custom_data argument to the name of the intent. This way the handler for the intent Yes can check whether the user confirmed the intent TurnOffLight or TurnOnLight: the value of custom_data gets passed during a session. If the function yes sees that custom_data has another value, this means that the user has triggered the intent Yes without being in a session for TurnOffLight or TurnOnLight.

Instead of ending the session with a message depending on custom_data, you can use the same approach in home automation applications, for instance for asking for confirmation before opening or closing a relay.

Try the example app continue_session.py.

Notifying the user

Your app’s voice actions aren’t limited to replying in a session. At any time, your app can inform the user of something without expecting a response. An example:

"""Example app to tell you the time every minute with a notification."""
import logging
import threading
from datetime import datetime

from rhasspyhermes_app import HermesApp

_LOGGER = logging.getLogger("TimeNotificationApp")
SECONDS = 60
SITE_ID = "default"


def tell_time():
    """Tell the time with a notification."""
    now = datetime.now().strftime("%H %M")
    app.notify(f"It's {now}", SITE_ID)
    threading.Timer(SECONDS, tell_time).start()


app = HermesApp("TimeNotificationApp")
threading.Timer(SECONDS, tell_time).start()
app.run()

This example starts a timer that notifies the user every minute with the current time. This is done using the rhasspyhermes_app.HermesApp.notify() method, which accepts a message and a site ID.

Try the example app time_app_notification.py.

Asyncio

Every function that you decorate with Rhasspy Hermes App should be defined with the async keyword. However, you don’t have to use the async functionality.

For apps which are time intensive by e.g. using database queries or API calls, we recommend the use of asynchronous functions. These allow your code to handle multiple requests at the same time and therefore cutting down on precious runtime.

Try the example app async_advice_app.py.

Other example apps

The GitHub repository has a couple of other example apps showing the library’s functionality:

Building an app on this library

If the API of this library changes, your app possibly stops working when it updates to the newest release of Rhasspy Hermes App. Therefore, it’s best to define a specific version in your requirements.txt file, for instance:

rhasspy-hermes-app==1.0.0

This way your app keeps working when the Rhasspy Hermes App adds incompatible changes in a new version.

The project adheres to the Semantic Versioning specification with major, minor and patch version.

Given a version number major.minor.patch, this project increments the:

  • major version when incompatible API changes are made;

  • minor version when functionality is added in a backwards-compatible manner;

  • patch version when backwards-compatible bug fixes are made.

More information

More information about the usage of the Rhasspy Hermes App library can be found in the API reference.