Giter VIP home page Giter VIP logo

rhasspy-hermes-app's Introduction

Rhasspy Hermes App

Continuous integration Documentation status PyPI package version Supported Python versions License

Rhasspy Hermes App is a helper library to create voice apps for Rhasspy in Python using the Hermes protocol

Requirements

Rhasspy Hermes App requires:

  • Python 3.7
  • Rhasspy 2.5

Installation

A package can be installed from PyPI:

pip3 install rhasspy-hermes-app

Documentation

Read the online documentation for more information about using the library, including the full API documentation with example code.

License

This project is provided by Koen Vervloesem as open source software with the MIT license. See the LICENSE file for more information.

rhasspy-hermes-app's People

Contributors

daenara avatar dependabot[bot] avatar h3adcra5h avatar jasonhildebrand avatar jonahkr avatar koenvervloesem avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

rhasspy-hermes-app's Issues

Add easier way to access slot values

See the forum. Quoting @DanielWe2:

One idea would be: add a new parameter “supported_slots” to the decorator.

Could look like this:

@app.on_intent("StartTimer", supported_slots=["minutes", "seconds"])
def handle_start_timer(intent, minutes, seconds):
or
def handle_start_timer(intent, slot_minutes, slot_seconds):

If the Intent has slot values for minutes or seconds handle_start_timer will get called with that otherwise it will provide “None”. One variant of this would be to also put the expected type into the decorator and let the decorator do the validation.

Another option: Just provide a helper function like mine in your module to get the slot values out of an intent. As you can see in my source (maybe I am doing it wrong) it is not that simple.

Example code from https://github.com/DanielWe2/rhasspy-hermes-app/blob/examples/timer_app.py with a modification from https://community.rhasspy.org/t/mqtt-error-coroutine-get-test-was-never-awaited/1436/21?u=koan):

def extract_slot_value(intent: NluIntent, slot_name: str, default=None):
    """extracts the value of a slot"""

    slot = next(filter(lambda slot: slot.slot_name == slot_name, intent.slots), None)
    if slot:
        return slot.value.get("value", default)
    return default

Usage in the intent handler function:

    minutes = extract_slot_value(intent, "minutes", 0)
    seconds = extract_slot_value(intent, "seconds", 0)

Proposal for language support

I created a component for homeassistant once and really likes the way they handle translations.
more detailed info >here
The basic file structure is for example:

  • skillDir /skill.py
  • skillDir / strings.json - this file contains a template(English) with all unique sentences
  • skillDir / lang - this directory contains all translation files
  • skillDir / lang / de.json - this file contains the german translated version of strings.json

Obviously we don't have the userbase to use platforms like Lokalize but this can just as easy be replaced by merging pullrequests to the skill repositories.

However the intent Json contains no information about which language is being used. One ugly way would be reading it from the profil but we still wouldn't know which profile is the active one.(if there are multiple ones) Maybe it would be possible to add a "lang" field in the Intent JSON with the language abbreviation (like en for english or de for german) on the rhasspy side of things.

These translation files could contain slots and sentences for an automatic implementation as well.

Document that `@app.on_topic()` cannot be used for topics that have dedicated decorators

The callbacks registered by the on_topic() decorator are currently processed last.
See

If a message on a topic for which a corresponding decorator exists (e.g. the on_intent decorator for the topic hermes/intent/<intent_name>) the topic callback is not executed at all.

Example:

@app.on_topic("hermes/intent/myIntent")
async def topic_callback(intent: NluIntent):
    # do something
    pass

The topic_callback is never called as

elif NluIntent.is_topic(topic):
matches first. In this case the intended callback topic_callback won't be found as it is not included in the _callbacks_intent but only in the _callbacks_topic dictionary.

This can be considered an expected behavior, but is not intuitive nevertheless, imho. Please document this limitation at least.

It would be even better, to change the overall behavior to allow subscribing to arbitrary topics with the on_topic decorator. This would even allow subscriptions for multiple intents as requested in #13 via @app.on_topic("hermes/intent/+"). Some rework of the overall message processing would be required though.

continueSession doesnt wait for the answer

Hi,
im working on a skill with dialouge, but it seems that continueSession doesnt wait for the answer. Maybe its a Rhasspy problem.
I end my first reaction with:

    return ContinueSession(
        text="Whats the title?", custom_data="title", intent_filter=["title"]
    )

it asks for the title, but it doesnt wait for my spoken answer or it fires some dump-intents for another skill. I hope someone could help me.

ERROR: Could not install packages due to an EnvironmentError

I run the pip3 install rhasspy-hermes-app command on my Ubuntu 20.04 virtualbox running on a windows 10 PC and get the following error message.

Defaulting to user installation because normal site-packages is not writeable Collecting rhasspy-hermes-app WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)'))': /packages/25/40/078fe07023a34b77321915b0313e9d41d1e024e79c5b23287bd7d19c8e6e/rhasspy-hermes-app-1.0.0.tar.gz WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)'))': /packages/25/40/078fe07023a34b77321915b0313e9d41d1e024e79c5b23287bd7d19c8e6e/rhasspy-hermes-app-1.0.0.tar.gz WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)'))': /packages/25/40/078fe07023a34b77321915b0313e9d41d1e024e79c5b23287bd7d19c8e6e/rhasspy-hermes-app-1.0.0.tar.gz WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)'))': /packages/25/40/078fe07023a34b77321915b0313e9d41d1e024e79c5b23287bd7d19c8e6e/rhasspy-hermes-app-1.0.0.tar.gz WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)'))': /packages/25/40/078fe07023a34b77321915b0313e9d41d1e024e79c5b23287bd7d19c8e6e/rhasspy-hermes-app-1.0.0.tar.gz ERROR: Could not install packages due to an EnvironmentError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Max retries exceeded with url: /packages/25/40/078fe07023a34b77321915b0313e9d41d1e024e79c5b23287bd7d19c8e6e/rhasspy-hermes-app-1.0.0.tar.gz (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1108)')))

Any ideas as to why this might be happening?

Raspberry PI OS Bullseye Python 3.9.2 Supported?

On a Raspberry Pi 4, I'm using Rhasspy 2.5.11 and processing the intents in my Python program using rhasspy-hermes-app. This was working fine when I was running the Buster version of the Raspberry Pi OS. I just upgraded to the recent Raspberry Pi OS that is using Bullseye. Now it has stopped working. When I give a voice command I get the following error:

ERROR:HermesApp:on_raw_message Traceback (most recent call last): File "/home/norwolf/.local/lib/python3.9/site-packages/rhasspyhermes_app/__init__.py", line 208, in on_raw_message nlu_intent = NluIntent.from_json(payload) File "/home/norwolf/.local/lib/python3.9/site-packages/dataclasses_json/api.py", line 72, in from_json kvs = json.loads(s, File "/usr/lib/python3.9/json/__init__.py", line 359, in loads return cls(**kw).decode(s) TypeError: __init__() got an unexpected keyword argument 'encoding'

Upgrading the Raspberry PI OS also changed the Python version from 3.7.3 to 3.9.2.

Is rhasspy-hermes-app supported on Bullseye and Python 3.9.2? Or am I experiencing a different problem?

Thanks

Continue Session/ Confirmation of command

Hello @koenvervloesem ,

i've tried to adopt your example for continuing a session or rather asking for confirmation of a command. However I ended up getting false detection of yes/ no (in my case german). That's why i added this to my sentences.ini:

[Yes]
Ja

[No]
Nein

this is my code:

from datetime import datetime
from time import sleep
from rhasspyhermes.nlu import NluIntent
from rhasspyhermes_app import ContinueSession, EndSession, HermesApp

from logger.logger_init import get_logger, setup_logging


_LOGGER = get_logger('Skills')
app = HermesApp("Skills")

@app.on_intent("GetTime")
async def get_time(intent: NluIntent):
    now = datetime.now().strftime("%H Uhr %M")
    return EndSession(f"Es ist {now}")

@app.on_intent("Yes")
async def yes(intent: NluIntent):
    print(intent.custom_data)
    if intent.custom_data == "SetAlarmOff":
        response = "Okay, ich schalte den Wecker aus"
    elif intent.custom_data == "SetAlarmOn":
        response = "Okay, ich schalte den Wecker ein"
    else:
        response = ""
    return EndSession(response)

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

@app.on_intent("SetAlarmOff")
async def set_alarm_off(intent: NluIntent):
    return ContinueSession(
        text=f"Möchtest du wirklich den Wecker ausschalten?", custom_data="SetAlarmOff"
    )
    
@app.on_intent("SetAlarmOn")
async def set_alarm_on(intent: NluIntent):
    return ContinueSession(
        text=f"Möchtest du wirklich den Wecker einschalten?", custom_data="SetAlarmOn"
    )

if __name__ == "__main__":
    # Setup logging
    setup_logging(default_filename = 'logger_config.json')
    get_logger(__name__).info(f'rhasspy-skills started!')
    
    # Start
    app.run()

no i've the problem, that the custom data isn't provided anymore. What am i doing wrong?

thank you very much for your support

greetings Nicolas

in TopicData site_id and session_id are the same

I need the caputred audio but also the session id, when i use:

@app.on_topic("rhasspy/asr/{SITE_ID}/{SESSION_ID}/audioCaptured")
async def test_topic1(data: TopicData, payload: bytes):
#some simple code

in data, session_id and site_id are both just the site_id.
best regards

@app.on_topic decorator not working with special characters (* is the one I came across)

Good Evening,

while trying to realize a small project I noticed some pretty strange behavior with the on_topic decorator. I need to receive non-hermes mqtt messages and after I tried to figure out what the best way is to do that, I thought I might try it that way first.

Here is what I tried:

@app.on_topic("weather/+/+/temperature_*C")
async def on_temperature_update(data: TopicData, payload: bytes):
    print(payload.decode('UTF-8'))

Does not work (HermesApp: Unexpected topic: weather/inside/wohnzimmer/temperature_*C)

@app.on_topic("weather/+/+/light_Lux")
async def on_temperature_update(data: TopicData, payload: bytes):
    print(payload.decode('UTF-8'))

Works

@app.on_topic("weather/outside/back/temperature_*C")
async def on_temperature_update(data: TopicData, payload: bytes):
    print(payload.decode('UTF-8'))

Works

Since it did work after I changed the temperature topic for the light one, I am pretty sure that it is the * in my topic that is the issue. According to the mqtt standard, only using + or # in a topic is not allowed, all other UTF-8 characters are fair game. Would it be possible to fix this?

I did some testing with escaping the * symbol when the regex is built and that got it to work, but I doubt my simple solution of replacing token + "$"with token.replace("*", "\*") + "$" in line 566 is a good solution. If this character causes problems when not escaped, others might too, so a more general approach of escaping characters might be the better solution for that.

Daenara

Change Standard Port to 12183 - Discussion

Since this App is rhasspy specific, it maybe would make sense to change the port from 1883 to 12183 which is the standard port rhasspy hosts its mqtt server on.

Altough it would make sense to keep it at 1883 if the user where to use a external mqtt broker.

Comment what you think about this.

KeyError: 'entity' on intent recognition

Hi,

thank you a lot for this great library. I tried to develop my own app using your work but unfortunately I get an error as soon as my intent is recognised:

[DEBUG:2020-06-06 15:39:35,283] asyncio: Using proactor: IocpProactor
[DEBUG:2020-06-06 15:39:35,284] OpenHABSwitchOnApp: Connected to MQTT broker
[DEBUG:2020-06-06 15:39:35,285] OpenHABSwitchOnApp: Subscribed to hermes/intent/SwitchOn
[ERROR:2020-06-06 15:39:39,386] HermesApp: on_raw_message
Traceback (most recent call last):
File "C:\Users\Arne\AppData\Roaming\Python\Python38\site-packages\rhasspyhermes_app\__init__.py", line 62, in on_raw_message
    nlu_intent = NluIntent.from_json(payload)
File "C:\Users\Arne\AppData\Roaming\Python\Python38\site-packages\dataclasses_json\api.py", line 78, in from_json
    return cls.from_dict(kvs, infer_missing=infer_missing)
File "C:\Users\Arne\AppData\Roaming\Python\Python38\site-packages\dataclasses_json\api.py", line 85, in from_dict
    return _decode_dataclass(cls, kvs, infer_missing)
File "C:\Users\Arne\AppData\Roaming\Python\Python38\site-packages\dataclasses_json\core.py", line 192, in _decode_dataclass
    init_kwargs[field.name] = _decode_generic(field_type,
File "C:\Users\Arne\AppData\Roaming\Python\Python38\site-packages\dataclasses_json\core.py", line 266, in _decode_generic
    res = _decode_generic(type_arg, value, infer_missing)
File "C:\Users\Arne\AppData\Roaming\Python\Python38\site-packages\dataclasses_json\core.py", line 254, in _decode_generic
    res = _get_type_cons(type_)(xs)
File "C:\Users\Arne\AppData\Roaming\Python\Python38\site-packages\dataclasses_json\core.py", line 297, in <genexpr>
    items = (_decode_dataclass(type_arg, x, infer_missing)
File "C:\Users\Arne\AppData\Roaming\Python\Python38\site-packages\dataclasses_json\core.py", line 150, in _decode_dataclass
    field_value = kvs[field.name]
KeyError: 'entity'

This is the corresponding intent:
[SwitchOn] schalte (das | den | die) $item{item} an

And this is my code:

import logging

from rhasspyhermes_app import HermesApp

_LOGGER = logging.getLogger("OpenHABSwitchOnApp")

app = HermesApp("OpenHABSwitchOnApp")


@app.on_intent("SwitchOn")
def switch_on(intent):

    return app.EndSession("test")


app.run()

Did I make any mistake in my app or is this a bug in the library?

Method for sending payloads?

Hi,
is there a method to send payloads e.g. for audio output? I'd like to stick to rhasspy-hermes-app, so if there is no method, this would be a suggestion for an enhacement.

thank you

GetTime example not working

Hi, I am trying to get the getTime example to work. However this error is thrown by the hermes library:


[DEBUG:2022-10-18 19:58:47,522] HermesApp: Namespace(host='#########', port=###, username='###', password='####', tls=False, tls_ca_certs=None, tls_certfile=None, tls_keyfile=None, tls_cert_reqs='CERT_REQUIRED', tls_version=None, tls_ciphers=None, site_id=None, debug=True, log_format='[%(levelname)s:%(asctime)s] %(name)s: %(message)s')
[DEBUG:2022-10-18 19:58:47,523] HermesApp: Connecting to ############
[DEBUG:2022-10-18 19:58:47,527] asyncio: Using selector: EpollSelector
[DEBUG:2022-10-18 19:58:47,529] test: Connected to MQTT broker
[DEBUG:2022-10-18 19:58:47,530] test: Subscribed to hermes/intent/testintent
[ERROR:2022-10-18 19:59:47,296] HermesApp: on_raw_message
Traceback (most recent call last):
  File "/home/v/.local/lib/python3.10/site-packages/rhasspyhermes_app/__init__.py", line 208, in on_raw_message
    nlu_intent = NluIntent.from_json(payload)
  File "/home/v/.local/lib/python3.10/site-packages/dataclasses_json/api.py", line 72, in from_json
    kvs = json.loads(s,
  File "/usr/lib64/python3.10/json/__init__.py", line 359, in loads
    return cls(**kw).decode(s)
TypeError: JSONDecoder.__init__() got an unexpected keyword argument 'encoding'

Can't get TTS to work

Evening,

I found this in the API documentation:
image

According to this it should speak what I put into EndSession as text, but my rhasspy keeps silent and I don't see any TTS command over mqtt.

Here is my full code:

import logging
from datetime import datetime

from rhasspyhermes.nlu import NluIntent

from rhasspyhermes_app import EndSession, HermesApp

from rhasspy_weather import weather

_LOGGER = logging.getLogger("SkillListener")

app = HermesApp("SkillListener", host="redacted", port=1883, username="redacted", password="redacted")


@app.on_intent("GetTime")
async def get_time(intent: NluIntent):
    """Tell the time."""
    hours = datetime.now().hour
    minutes = datetime.now().minute
    if minutes == 0:
        return EndSession(f"Es ist {hours} Uhr.")
    return EndSession(f"Es ist {hours} Uhr {minutes}.")


@app.on_intent("GetWeatherForecast")
@app.on_intent("GetWeatherForecastTemperature")
@app.on_intent("GetWeatherForecastCondition")
@app.on_intent("GetWeatherForecastItem")
async def get_weather(intent: NluIntent):
    """Get weather"""
    forecast = weather.get_weather_forecast(intent, config_path="../rhasspy_weather_config.ini")
    return EndSession(text=forecast)


app.run()

This is what I see on the mqtt server after my intent is finished:

image

Did I miss anything in trying to get tts to work, is there an error in the documentation and it should not do tts or did I strumble upon a bug?

Automatically create service for app

The library could come with a command-line program that can automatically:

  • create a systemd service file for an app
  • create a Dockerfile for an app

This makes it easier to run an app in the background.

Issue listening for playBytes topic

I am trying to move audio playing from Rhasspy to my own intent handling app built using rhasspy-hermes-app. I figured the example here would be a great place to start.

Unfortunately, I can't even get that example app to function properly. When I run it and speak the wake word, I get the following output:

/home/pi/.local/lib/python3.7/site-packages/rhasspyhermes_app/__init__.py:259: RuntimeWarning: coroutine 'HermesApp.on_topic.<locals>.wrapper.<locals>.wrapped' was never awaited
  function_2(data, payload)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

I'm pretty new to Python so this is a bit challenging for me to debug. If someone could point me in the right direction that would be highly appreciated.

Add a way to subscribe to all intents with a prefix

Something like this could be interesting for some use case:

@app.on_intent("megaApp", prefix = True)

This would then subscribe to intents megaApp/GetTime, megaApp/GetDate and so on.

Suggested by @JonahKr

Does anyone have a use case for this?

Encoding Issue

This seems to have a little encoding issue along the way.

My weather script outputs the sentence
Nein, morgen gibt es keinen Regen. Das Wetter ist: Bedeckt und mäßiger Wind

and once it is sent over mqtt by this it ends up as
image

If I send the sentence over mqtt with my own script, it ends up as it should.

I am pretty sure that this is a missing ensure_ascii=False in at least one json.dumps.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.