Turn off light on a timer

I’m brand new to AppDaemon and writing my first app. I’m trying to set a timer on a light when it turns on. That part works, but it sets the timer 2 times when the light turns on. Any ideas?

Here’s the code:

import appdaemon.plugins.hass.hassapi as hass
import appdaemon.plugins.hass

class LightTimer(hass.Hass):
    def initialize(self):
        try:
            self._entity_id = self.args.get('entity_id', None)
            self._timeout = int(self.args.get('timeout', 0))
        except (TypeError, ValueError):
            self.log("There's a problem with the configuration.", level="ERROR")
            return

        # You must specify an entity
        if self._entity_id is None:
            self.log('The entity is missing.', level="ERROR")
            return

        # Only accepts on and off values for entity_id
        state = self.get_state(self._entity_id)
        if state not in ['off', 'on']:
            self.log("Invalid state: {}. Expecting either on or off.".format(light_state), level="WARNING")
            return

        # if no time is specified, set it to 60 seconds.
        if self._timeout == 0:
            self._timeout = 60

        # Listen for the light to turn on
        if self._entity_id is not None and self._timeout is not None:
            self.listen_state(self.light_turned_on, self._entity_id, new="on")

        # Done
        self.log("Ready to go")

    # When the light turns on, start a timer.
    def light_turned_on(self, entity, attribute, old, new, kwargs):
        self.log('The current state is ' + self.get_state(self._entity_id))

        if self.get_state(self._entity_id) == 'on':
            self.start_timer()

    # Set the entity to turn off after the timeout is reached.
    def start_timer(self):
        self._timer = self.run_in(self.turn_off_light, self._timeout)
        self.log('Timer set for ' + str(self._timeout) + ' seconds.')

    # Turn off the light when the timer expires
    def turn_off_light(self, kwargs):
        self.log('Turning off ' + self._entity_id)
        self._timer = None
        self.turn_off(self._entity_id)
        self.log(self._entity_id + ' turned off.')

Here’s how I’m calling it:

turn_off_office_light:
  module: lighttimer
  class: LightTimer
  entity_id: light.office
  timeout: 60

Here’s the log file:

2019-12-22 14:42:13.336439 INFO AppDaemon: Reloading Module: /config/appdaemon/apps/lighttimer.py
2019-12-22 14:42:13.338480 INFO AppDaemon: Initializing app turn_off_office_light using class LightTimer from module lighttimer
2019-12-22 14:42:13.340246 INFO turn_off_office_light: Ready to go
2019-12-22 14:42:20.561491 INFO turn_off_office_light: The current state is on
2019-12-22 14:42:20.562568 INFO turn_off_office_light: Timer set for 60 seconds.
2019-12-22 14:42:23.347497 INFO turn_off_office_light: The current state is on
2019-12-22 14:42:23.348557 INFO turn_off_office_light: Timer set for 60 seconds.
2019-12-22 14:43:20.003136 INFO turn_off_office_light: Turning off light.office
2019-12-22 14:43:20.019176 INFO turn_off_office_light: light.office turned off.

I was able to get it working by adding old=“off” to the listener.

I tried to add support for passing more than one entity so I can specify multiple switches to turn off at different intervals. Unfortunately, when the light turns on, it immediately turns off. It’s like the delay is being ignored. Any ideas?

import appdaemon.plugins.hass.hassapi as hass
import appdaemon.plugins.hass

class LightTimer(hass.Hass):

    def initialize(self):

        try:
            self._entities = self.args.get('entities', None)
        except (TypeError, ValueError):
            self.log("There's a problem with the configuration.", level="ERROR")
            return

        # You must specify an entity
        if self._entities is None:
            self.log('No entity is specified.', level="ERROR")
            return

        for entity in self._entities:
            entity_name = entity["entity"]
            state = self.get_state(entity_name)
            
            if state not in ['off', 'on']:
                self.log("Invalid state for {}: {}. Expecting either on or off.".format(entity_name, state), level="WARNING")
                continue

            # self._timer = None
            self.listen_state(self.entity_turned_on, entity_name, new="on", old="off")

        # Done
        self.log("Ready to go")

    # When the light turns on, start a timer.
    def entity_turned_on(self, entity, attribute, old, new, kwargs):
        
        passedEntity = entity
        self.log("The entity was turned on: " + passedEntity)

        for entity in self._entities:
            entity_name = entity["entity"]
            if entity_name == passedEntity:
                try:
                    timeout = entity["timeout"]
                except:
                    self.log('No timeout given. 60 seconds will be used by default.', level="WARNING")
                    timeout = 60
                self.log("Setting a timer for " + entity_name + " for " + str(timeout))
                self.start_timer(entity_name, timeout)

    def start_timer(self, entity_name, timeout):
        self.log("Starting a timer for " + str(timeout) + " seconds")
        self.handle = self.run_in(self.turn_off_entity(entity_name), int(timeout))

    # Turn off the light when the timer expires
    def turn_off_entity(self, entity):
        self.log("Turning off light")
        self.turn_off(entity)
        self.handle = None

And in the apps.yaml:

turn_off_lights:
  module: lighttimer
  class: LightTimer
  entities: 
    - entity: light.office
      timeout: 10