Appdaemon countdown timer (UI)

Hey Andrew,

In a previous post (An example of a countdown timer, and some related questions) you mentioned that would you be able to quickly whip up an example of a countdown timer using appdaemon. I would like to be able to show the timers on the frontend for when the lights are going to turn off (motion sensor). Is there any chance you could point me in the correct direction for tracking the timers from appdaemon in the UI?

Cheers
Zak

I would approach this by creating a template sensor, then setting its state from AppDaemon each time the countdown ticked.

Revisiting this thread for @dangeek - I took a crack at this:

 import appdaemon.appapi as appapi

"""
    App to turn lights on when motion detected then off again after a delay while updating sensor for timer status
    
    Use with constrints to activate only for the hours of darkness
    
    Args:
    
    sensor: binary sensor to use as trigger
    entity_on : entity to turn on when detecting motion, can be a light, script, scene or anything else that can be turned on
    entity_off : entity to turn off when detecting motion, can be a light, script or anything else that can be turned off. Can also be a scene which will be turned on
    delay: amount of time after turning on to turn off again. If not specified defaults to 60 seconds.
    countdown: Name of a sensor to be used to show the remaining time before the light is turned off (does not need to exist in HASS will be created automatically)
    
    Release Notes
    
    Version 1.2:
      Add sensor countdown
    
    Version 1.1:
      Add ability for other apps to cancel the timer
    
     Version 1.0:
       Initial Version
"""

class MotionLights(appapi.AppDaemon):

  def initialize(self):
    
    self.handle = None
    
    # Check some Params

    # Zero out the countdown
    self.set_countdown("-")
    # Subscribe to sensors
    if "sensor" in self.args:
      self.listen_state(self.motion, self.args["sensor"])
    else:
      self.log("No sensor specified, doing nothing")

    if "delay" in self.args:
      self.delay = self.args["delay"]
    else:
      self.delay = 60

    self.count = self.delay
    
  def motion(self, entity, attribute, old, new, kwargs):
    if new == "on":
      self.cancel()

      if "entity_on" in self.args:
        self.log("Motion detected: turning {} on".format(self.args["entity_on"]))
        self.turn_on(self.args["entity_on"])

      now = self.datetime()
      self.handle = self.run_every(self.light_check, now, 1)
  
  def light_check(self, kwargs):
    self.count -= 1
    self.log(self.count)
    self.set_countdown(self.count)
    if self.count <= 0:
      if "entity_off" in self.args:
          self.log("Turning {} off".format(self.args["entity_off"]))
          self.turn_off(self.args["entity_off"])
      self.cancel()

  def cancel(self):
    self.count = self.delay
    self.cancel_timer(self.handle)
    self.set_countdown("-")

  def set_countdown(self, value):
    if "countdown" in self.args:
      self.set_state(self.args["countdown"], state = value)

Here is an example of the configuration:

motion_lights:
  class: MotionLights
  module: motion_lights
  sensor: binary_sensor.upstairs_sensor
  countdown: sensor.motion_countdown
  entity_on: group.office_light
  entity_off: group.office_light
  delay: 20
2 Likes