I am trying to move my NodeRed scenarios to HA using AppDaemon. I have MQTT messages I would like to listen to but AFAICT there is nothing built-in for that.
I decided to use MQTT Paho in my AppDaemon code but I am not sure whether I can use a blocking call (namely loop_forever()):
import appdaemon.plugins.hass.hassapi as hass
import paho.mqtt.client as mqtt
class HelloWorld(hass.Hass):
def initialize(self):
self.log("Hello from AppDaemon")
client = mqtt.Client()
client.on_message = self.alarm
client.connect("mqtt.example.com")
res = client.subscribe("maison/hello")
client.loop_forever()
def alarm(self, *kwargs):
self.log("alarm raised")
self.log(kwargs)
This code works (alarm is fired when I publish something on maison/hello) but:
I did not try to start another app yet
I do not know if using a blocking call is not interfering with HA, overall
No, you can’t do that. If you do, you will lock up appdaemon’s utility loop which is a very bad thing.
If you want to do this as things stand, you would need to create your own thread to do the run forever piece, initialize() can’t be held up, nor can any of the worker threads for callbacks.
The proper way to support this kind of activity is to write a new plugin which will allow a much deeper integration, and is intended to manage asynchronous events and blocking calls like that.
Luckily, there is already a pull request in place for a basic MQTT listener contributed by github user tschmidty69. I haven;t got around to looking at it yet but I will do soon as I think this is an excellent use for the new plugin functionality in AppDaemon 3.0.
Yes, I’ve done that before in my apps - only trick is to make sure you terminate it cleanly when the app is restarted using the terminate() function or you may get strange things happening.
There is no way to kill a thread in a portable way. Fortunately, loop_forever is shut down upon a client disconnect() .
I just upgraded client to a property (self.client) and issue a self.client.disconnect() in terminate(). This said, when shutting down the app daemon via Ctrl-C I did not see it being used. This is only a dev environment, though, so in prod (on HA) it will be more useful if I ever need to stop the app (which is not my use case)
Correct - ctrl-c ends everything as quickly as possible and since it assumes its being run from the command line it doesn’t attempt any cleanup of the apps, the same is true when stopped form sysctl I assume - I’m not sure what mechanism is used to stop the process under those conditions, but the assumption is that everything will be cleaned up when the process terminates.
I know you’re solving this in a different way, but it’s probably worth knowing that paho-mqtt already has a threaded interface. You can use loop_start() and loop_stop() and the library will manage running the loop in a background thread for you.
In systemd one can define how a service is stopped, via the ExecStop entry. This can be a kill which will send a signal which can then be trapped in the application.
So if appdaemon reacts to some signal and shuts down cleanly (including terminate()) this can be configured.
It wouldn’t be to hard to add app termination code to the shutdown logic I already have - then I could easily define multiple signals to force it to run.
Very soon in fact - this turned out to be a lot easier than I thought. Now, ctrl-c will also force apps to terminate, and sending a SIGTERM to the process will make it shutdown cleanly, runnin terminates() etc.
In both cases, the apps will be terminated in revers order of any dependencies that they have.