AppDaemon exception I don't understand

Hi all,

I have the following code that works (the turn_off_in turns off the light in 5 seconds):

        self.turn_on("light.eetkamer_spots_1")
        self.turn_off_in("light.eetkamer_spots_1", 5)

The implementation of turn_off_in is as follows:

    def turn_off(self, *args):
        self.hass.turn_off(*args)

    def turn_off_in(self, entity_id, delay):
        self.run_in(self.turn_off_handler, delay, entity_id=entity_id)

    def turn_off_handler(self, kwargs):
        self.turn_off(kwargs["entity_id"])

I have this method:

            self.turn_on()
            self.turn_off_in(self.get_mosquitto_switch(), 3600)

which fails with the following exception and I don’t understand why:

2019-10-14 23:26:27.153933 INFO room_automation: Antimuggen automation slaapkamer_2 only started for one hour due to window closed.
2019-10-15 00:26:27.164018 WARNING AppDaemon: ------------------------------------------------------------
2019-10-15 00:26:27.164300 WARNING AppDaemon: Unexpected error in worker for App room_automation:
2019-10-15 00:26:27.164506 WARNING AppDaemon: Worker Ags: {'name': 'room_automation', 'id': UUID('8e9f6d8e-25f9-416a-9576-a742f259692f'), 'type': 'timer', 'function': <bound method AbstractModule.turn_off_handler of <room_automation.MosquittoKiller object at 0x7faca806d198>>, 'kwargs': {'entity_id': 'switch.slaapkamer_2_antimuggen'}}
2019-10-15 00:26:27.164687 WARNING AppDaemon: ------------------------------------------------------------
2019-10-15 00:26:27.165295 WARNING AppDaemon: Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/appdaemon/appdaemon.py", line 586, in worker
    funcref(self.sanitize_timer_kwargs(app, args["kwargs"]))
  File "/conf/apps/room_automation.py", line 84, in turn_off_handler
    self.turn_off(kwargs["entity_id"])
TypeError: turn_off() takes 1 positional argument but 2 were given

2019-10-15 00:26:27.165548 WARNING AppDaemon: ------------------------------------------------------------

Thanks in advance for your help…

Is this an app using MQTT Plugin and HASS plugin at the same time?

Can you please show the code for the “get_mosquitto_switch” method?

self.turn.off is a built-in convenience function in Appdaemon. If you are trying to override it with your own implementation it would create a name clash of some sort I would expect. Try renaming the turn_off function.

I thought the same, however he says it works in the first case. Anyway, shouldn’t his turn_off method just override the existing turn_off method of AppDaemon? I did it the same way for listen_event to wrap it with a constraint.

What I don’t understand, @rdehuyss, why did you define the turn_off method ? You don’t change anything of the original turn_off method, just create another layer.

No MQTT…

The code for get_mosquitto_switch is easy (room_name) is a string:

     def get_mosquitto_switch(self):
       return "switch." + self.room_name + "_antimuggen"

I’m not overriding it, I have a wrapper class around hass and delegate to it. So, a name clash is not possible in my eyes.

So:

class AbstractModule:

    def __init__(self, hass: hass.Hass):
        self.hass = hass
...
    def turn_on(self, entity_id):
        self.hass.turn_on(entity_id)

    def turn_on_in(self, entity_id, delay):
        self.run_in(self.turn_on_handler, delay, entity_id=entity_id)

    def turn_on_handler(self, kwargs):
        self.turn_on(kwargs["entity_id"])

    def turn_off(self, *args):
        self.hass.turn_off(*args)

    def turn_off_in(self, entity_id, delay):
        self.run_in(self.turn_off_handler, delay, entity_id=entity_id)

    def turn_off_handler(self, kwargs):
        self.turn_off(kwargs["entity_id"])

    def toggle(self, *args):
        self.hass.toggle(*args)

    def run_in(self, callback, delay, **kwargs):
        return self.hass.run_in(callback, delay, **kwargs)
...

Some more background: this is how my apps.yml looks (I like it in one file):

room_automation:
  module: room_automation
  class: RoomAutomation
  rooms:
    bureau:
      switch_to_light: 
        - bureau_spots_1
        - bureau_spots_2
        - buitenverlichting_zijkant
        - tuinverlichting
    gang:
      switch_to_light:
        - gang_spots,gang_spots_beneden -> gang_spots
    berging:
      switch_to_light:
        - berging_licht
    kitchen:
      switch_to_light:
        - keuken_spots
        - keuken_tafellamp
        - terras_verlichting
    livingroom:
      switch_to_light:
        - living_spots
        - living_leesverlichting
      zonwering: verdieping_1_zonwering
      climate: 
        thermostat: thermostaat_living
        schedule:
          '06:30:00': 21
          '21:30:00': 16
    toilet_1:
      switch_to_light:
        - toilet_1_licht
    badkamer:
      switch_to_light:
        - badkamer_licht -> badkamer_licht_1
      velux: badkamer_velux
      zonwering: badkamer_zonwering
      climate: 
        thermostat: thermostaat_badkamer
        occupancy_temperature: 21
        schedule:
          '06:00:00': 21
          '07:30:00': 16
          '19:30:00': 21
          '21:30:00': 16
      media_player: media_player.badkamer

I use convention over configuration, meaning that often, my switches (binary sensors) are called binary_sensor.bureau_spots_1 and my light is thus switch.bureau_spots_1.

For each of the big blocks I created a class, e.g. class MosquittoKiller(AbstractModule): which extends the AbstractModule wrapper.

In this MosquittoKiller (which turns on and off something a plugin agains mosquitto’s), I want to turn it off after an hour. And that’s where the exception happens…

I would add a debug statement in your turn_off() method to print the value of *args. Then you’ll see exactly what’s being passed to self.hass.turn_off()

1 Like

I assume you mean in the turn_off_handler as I on probably won’t reach the turn_off method of I read the exception log?

What do you get in the logs when you change turn_off_handler to this:

def turn_off_handler(self, kwargs):
        self.log(kwargs["entity_id"])
        self.turn_off(kwargs["entity_id"])

@Burningstone, you helped me already a lot: I can reproduce it!

Here is what I get then:

2019-10-21 21:53:06.976712 INFO room_automation: Antimuggen automation slaapkamer_3 started manually by a user for 10 seconds.
2019-10-21 21:53:16.165244 INFO room_automation: switch.slaapkamer_3_antimuggen
2019-10-21 21:53:16.165567 WARNING AppDaemon: ------------------------------------------------------------
2019-10-21 21:53:16.165779 WARNING AppDaemon: Unexpected error in worker for App room_automation:
2019-10-21 21:53:16.166011 WARNING AppDaemon: Worker Ags: {'name': 'room_automation', 'id': UUID('808199c0-f160-40d7-9632-d8c266816e75'), 'type': 'timer', 'function': <bound method AbstractModule.turn_off_handler of <room_automation.MosquittoKiller object at 0x7faca80fc908>>, 'kwargs': {'entity_id': 'switch.slaapkamer_3_antimuggen'}}
2019-10-21 21:53:16.166396 WARNING AppDaemon: ------------------------------------------------------------
2019-10-21 21:53:16.167718 WARNING AppDaemon: Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/appdaemon/appdaemon.py", line 586, in worker
    funcref(self.sanitize_timer_kwargs(app, args["kwargs"]))
  File "/conf/apps/room_automation.py", line 85, in turn_off_handler
    self.turn_off(kwargs["entity_id"])
TypeError: turn_off() takes 1 positional argument but 2 were given

2019-10-21 21:53:16.167981 WARNING AppDaemon: ------------------------------------------------------------

The log output looks correct in my eyes: 2019-10-21 21:53:16.165244 INFO room_automation: switch.slaapkamer_3_antimuggen

I still then don’t understand why the code works in other places :thinking:

Digging deeper…

Never mind - it is a PEBCAC problem :slight_smile:

You guys were giving all the right hints but my euro did not fall: the AbstractModule has a turn_off method which accepts self and the entity_id as a parameter but I override it in the MosquittoKiller where it only accepts self as a parameter.

As a Java/.NET developer I’m still learning the corner cases of Python - I do miss method overloading…

Thanks to all for the wonderful help and brainstorming!

1 Like