Systemd / systemctl

Hello,

I’m trying to manage systemd motion service via appdaemon.
I want to stop or start motion.service if I’m at home or not, but I have some stability problem.

Does anyone have already make such a thing ?

Here is my first try :

import appdaemon.appapi as appapi
import paho.mqtt.client as mqtt
import time
from systemd_dbus.manager import Manager

class MotionService(appapi.AppDaemon):

    def initialize(self):
        self.log("Motion Service Starting...")
        self.client = mqtt.Client("appdaemon")
        self.client.on_connect = self.mqtt_on_connect
        self.client.on_message = self.mqtt_on_message
        self.manager = Manager()
        self.run()

    def run(self):
        self.client.connect("127.0.0.1", 1883, 60)
        self.client.subscribe("/PI/LivingRoom/motion/Set", 0)
        self.client.loop_start()

    def terminate(self):
        self.client.loop_stop()

    def mqtt_on_connect(self, client, userdata, rc,a):
        self.log("Connect: {}".format(str(rc)))

    def mqtt_on_message(self, client, userdata, msg):
        payload = msg.payload.decode("utf-8")
        self.log("Topic:{}\n menssage:{}".format(msg.topic,payload))

        status = ''
        unit = self.manager.get_unit('motion.service')
        if unit.properties.ActiveState == 'active':
            status = "ON"
        else:
            status = "OFF"

        if str(payload) == "ON" and str(status) == "OFF":
            unit.start('fail')
            time.sleep(1)
        elif str(payload) == "OFF" and str(status) == "ON":
            unit.stop('fail')
            time.sleep(1)

        self.call_service("mqtt/publish", topic = "/PI/LivingRoom/motion", payload = str(payload), qos = "0", retain = "false" )

It works on first start. But sometimes appdaemon reload modules, when home assistant was down, or when wifi was down, and it fails. Here is the log :

Sepp 18 22:04:46 localhost appdaemon[760]: 2017-09-18 22:04:46.754195 INFO motion: Motion Service Starting...
Sep 18 22:04:46 localhost appdaemon[760]: Exception in thread Thread-11:
Sep 18 22:04:46 localhost appdaemon[760]: Traceback (most recent call last):
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/systemd_dbus/exceptions.py", line 42, in wrapper
Sep 18 22:04:46 localhost appdaemon[760]: return func(*args, **kwargs)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/systemd_dbus/unit.py", line 164, in start
Sep 18 22:04:46 localhost appdaemon[760]: job_path = self.__interface.Start(mode)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/dbus/proxies.py", line 145, in __call__
Sep 18 22:04:46 localhost appdaemon[760]: **keywords)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/dbus/connection.py", line 651, in call_blocking
Sep 18 22:04:46 localhost appdaemon[760]: message, timeout)
Sep 18 22:04:46 localhost appdaemon[760]: dbus.exceptions.DBusException: org.freedesktop.systemd1.TransactionIsDestructive: Transaction is destructive.
Sep 18 22:04:46 localhost appdaemon[760]: During handling of the above exception, another exception occurred:
Sep 18 22:04:46 localhost appdaemon[760]: Traceback (most recent call last):
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/lib/python3.4/threading.py", line 920, in _bootstrap_inner
Sep 18 22:04:46 localhost appdaemon[760]: self.run()
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/lib/python3.4/threading.py", line 868, in run
Sep 18 22:04:46 localhost appdaemon[760]: self._target(*self._args, **self._kwargs)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/paho/mqtt/client.py", line 2606, in _thread_main
Sep 18 22:04:46 localhost appdaemon[760]: self.loop_forever(retry_first_connection=True)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/paho/mqtt/client.py", line 1470, in loop_forever
Sep 18 22:04:46 localhost appdaemon[760]: rc = self.loop(timeout, max_packets)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/paho/mqtt/client.py", line 995, in loop
Sep 18 22:04:46 localhost appdaemon[760]: rc = self.loop_read(max_packets)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/paho/mqtt/client.py", line 1273, in loop_read
Sep 18 22:04:46 localhost appdaemon[760]: rc = self._packet_read()
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/paho/mqtt/client.py", line 1838, in _packet_read
Sep 18 22:04:46 localhost appdaemon[760]: rc = self._packet_handle()
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/paho/mqtt/client.py", line 2285, in _packet_handle
Sep 18 22:04:46 localhost appdaemon[760]: return self._handle_publish()
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/paho/mqtt/client.py", line 2456, in _handle_publish
Sep 18 22:04:46 localhost appdaemon[760]: self._handle_on_message(message)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/paho/mqtt/client.py", line 2603, in _handle_on_message
Sep 18 22:04:46 localhost appdaemon[760]: self.on_message(self, self._userdata, message)
Sep 18 22:04:46 localhost appdaemon[760]: File "/home/pi/github/appdaemon/conf/apps/motion.py", line 39, in mqtt_on_message
Sep 18 22:04:46 localhost appdaemon[760]: unit.start('fail')
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/systemd_dbus/exceptions.py", line 44, in wrapper
Sep 18 22:04:46 localhost appdaemon[760]: raise SystemdError(error)
Sep 18 22:04:46 localhost appdaemon[760]: systemd_dbus.exceptions.SystemdError: TransactionIsDestructive(Transaction is destructive.)
Sep 18 22:04:46 localhost appdaemon[760]: 2017-09-18 22:04:46.779486 WARNING ------------------------------------------------------------
Sep 18 22:04:46 localhost appdaemon[760]: 2017-09-18 22:04:46.780419 WARNING Unexpected error during loading of motion:
Sep 18 22:04:46 localhost appdaemon[760]: 2017-09-18 22:04:46.781053 WARNING ------------------------------------------------------------
Sep 18 22:04:46 localhost appdaemon[760]: 2017-09-18 22:04:46.789305 WARNING Traceback (most recent call last):
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/systemd_dbus/exceptions.py", line 42, in wrapper
Sep 18 22:04:46 localhost appdaemon[760]: return func(*args, **kwargs)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/systemd_dbus/manager.py", line 342, in subscribe
Sep 18 22:04:46 localhost appdaemon[760]: self.__interface.Subscribe()
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/dbus/proxies.py", line 70, in __call__
Sep 18 22:04:46 localhost appdaemon[760]: return self._proxy_method(*args, **keywords)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/dbus/proxies.py", line 145, in __call__
Sep 18 22:04:46 localhost appdaemon[760]: **keywords)
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/dbus/connection.py", line 651, in call_blocking
Sep 18 22:04:46 localhost appdaemon[760]: message, timeout)
Sep 18 22:04:46 localhost appdaemon[760]: dbus.exceptions.DBusException: org.freedesktop.systemd1.AlreadySubscribed: Client is already subscribed.
Sep 18 22:04:46 localhost appdaemon[760]: During handling of the above exception, another exception occurred:
Sep 18 22:04:46 localhost appdaemon[760]: Traceback (most recent call last):
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 911, in read_app
Sep 18 22:04:46 localhost appdaemon[760]: init_object(name, class_name, module_name, conf.app_config[name])
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 582, in init_object
Sep 18 22:04:46 localhost appdaemon[760]: conf.objects[name]["object"].initialize()
Sep 18 22:04:46 localhost appdaemon[760]: File "/home/pi/github/appdaemon/conf/apps/motion.py", line 13, in initialize
Sep 18 22:04:46 localhost appdaemon[760]: self.manager = Manager()
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/systemd_dbus/manager.py", line 42, in __init__
Sep 18 22:04:46 localhost appdaemon[760]: self.subscribe()
Sep 18 22:04:46 localhost appdaemon[760]: File "/usr/local/lib/python3.4/dist-packages/systemd_dbus/exceptions.py", line 44, in wrapper
Sep 18 22:04:46 localhost appdaemon[760]: raise SystemdError(error)
Sep 18 22:04:46 localhost appdaemon[760]: systemd_dbus.exceptions.SystemdError: AlreadySubscribed(Client is already subscribed.)
Sep 18 22:04:46 localhost appdaemon[760]: 2017-09-18 22:04:46.790218 WARNING ------------------------------------------------------------
Sep 18 22:04:46 localhost appdaemon[760]: 2017-09-18 22:04:46.793625 INFO App initialization complete
Sep 18 22:04:47 localhost appdaemon[760]: 2017-09-18 22:04:47.521643 INFO Connected to Home Assistant 0.53.1
Sep 18 22:04:47 localhost appdaemon[760]: 2017-09-18 22:04:47.735995 WARNING Scheduler clock skew detected - delta = 3.4630050659179688 - resetting

Does anyone can help me on this ? Thx a lot.

seems like the termination isnt going like it should.
connection isnt closed and you get an error when you try to subscribe again.

cant you make a check if the connection already exists?
and only subscribe and connect if there is no connection?
in that case you even dont need the terminate and the loop keeps running even when AD restarts.

I would create an MQTT binary sensor in HA to receive the MQTT topics, and then trigger of the state of that sensor.

This would have the advantage of making the state of the sensor visible in HA, and make your appdaemon app much simpler.