After the discussion about Rhasspy apps with Hermes MQTT in AppDaemon I did a first proof of concept of an ‘app library’ in Python. This makes it possible to write the toy app from that discussion as a standalone Python script like this (with the right import statements of course):
class WhatsTheTime(RhasspyHermesMQTTApp):
@Intent("GetTime")
def get_time(self, intent):
now = datetime.now().strftime("%H %M")
self.say(f"It's {now}", intent.site_id)
if __name__ == "__main__":
WhatsTheTime()
And as an AppDaemon app like this:
class WhatsTheTime(RhasspyHermesAppDaemonApp):
@Intent("GetTime")
def get_time(self, intent):
now = self.datetime().strftime("%H %M")
self.say(f"It's {now}", intent.site_id)
I hate boilerplate code, so this is a big improvement in usability for app developers.
To make this usable, I should implement decorators (like the @Intent
) for every Hermes MQTT message an app can react to, and methods (like the say
) for every Hermes MQTT message an app can send (for instance I would have to end the current session in this example instead of just using the tts/say
MQTT topic that the say
method emits). This is quite doable because the Rhasspy Hermes library does all the heavy lifting.
Any comments about the API design or specific requirements before I start working on a first version?
One issue I’m not sure yet how to deal with is how to make it possible to reuse the same code to run as a standalone Python app (optionally in a Docker container) or as an AppDaemon app. Right now I have defined base classes for the first and second situation, and in the second situation the base app (RhasspyHermesAppDaemonApp
) inherits from AppDaemon’s mqtt.Mqtt
class. As you see from the two examples above, the standalone and AppDaemon versions are almost identical. You only inherit from a different base class, and in the AppDaemon case you use another function for the datetime
because that’s what AppDaemon recommends. You also have access to all AppDaemon’s state in the latter case (not used in this example). But in most cases the method body would be exactly the same in both situations if you’re not using any functionality specific to the execution environment (for instance subscribing to non-Hermes MQTT messages or using AppDaemon’s internal state or methods).
I don’t think it’s possible to just write an app and decide at runtime whether you want to run it in AppDaemon or standalone. But as far as I can see now, it should be possible to write an API that is almost exactly the same for app developers on AppDaemon or standalone Python, they just have to choose a base class.
I’m also not sure yet how to publish the library practically: one library where you just import the right base class for your execution environment? But then you only use half of the library. So maybe make it two libraries, one for AppDaemon and one for standalone Python, but with (almost) exactly the same API? That seems more difficult to maintain, but is probably nicer for the app developer.