Loops in conditions

I am having an automation that will trigger a turn off our lights at 22.30 every evening. I would however like that I could put in some kind of loop so it when the time is 22.30 it checks if “sensorId” has registrered any movement with in the last 15 minutes, if yes wait 15 minutes and run again.

Currently my off automation just looks like this:

 def time (self, entity, attribute, old, new, kwargs): 
    self.turn_off("light.aeotec_zw111_nano_dimmer_level")

I am unsure if I should use a “while” or self.run_in() wrapped somehow? Also how do I get the last time a motion sensor has been on?

you could use a run_daily for 22:30
in the callback you call another function that in itself has a run_in(callback,15*60
and in the callback you check if there was any motion in the last 15 mins.
the check for the last motion could be notated with a listen_state.
here is some pseudo code that would get you there:

import datetime

def initialise(...):
    self.lastmotion = datetime.datetime.now()
    self.listen_state(self.motion_there, "sensor.motion", new = "on")
    starttime = datetime.time(22, 30, 00)
    self. run_daily(self.check_now,starttime)

def motion_there(....):
    self.last_motion = datetime.datetime.now()

def check_now(...):
    time_diff = datetime.datetime.now() - self.last_motion
    if time_diff > 15 minutes:
        # do the stuff you want
    else:
        self.run_in(self.check_now,15 * 60)

its not wise to use a while loop, because then the thread will continue to run and wont be given free to other stuff. when you create a few of those kind of apps, AD will start to hang itself.

1 Like

Thanks a lot that was not at all the direction I was headed, so nice to have some guidance.

I modified the code slightly:

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

class lux_dimming(hass.Hass):

  def initialize(self):
   self.listen_state(self.lux_low,"sensor.fibaro_system_fgms001zw5_motion_sensor_luminance")    
   self.last_motion = datetime.datetime.now()
   self.listen_state(self.motion_there, "binary_sensor.fibaro_system_fgms001zw5_motion_sensor_sensor_3", new = "off")
   starttime = datetime.time(22, 00, 00)
   self.run_daily(self.check_now,starttime)
  

  def lux_low (self, entity, attribute, old, new, kwargs):
if float(old) > 150 and float(new) <=150:
    self.turn_on("light.aeotec_zw111_nano_dimmer_level")
    self.notify("Køkken Ø tændt", name = "telegram")
if (float(old) <= 150 and float(new) >150) and self.get_state("light.aeotec_zw111_nano_dimmer_level") == "on":
    self.turn_off("light.aeotec_zw111_nano_dimmer_level")
    self.notify("Køkken Ø slukket", name = "telegram")

  def motion_there(self, kwargs):
self.last_motion = datetime.datetime.now()

  def check_now(self,kwargs):
time_diff =datetime.timedelta.total_seconds( datetime.datetime.now() - self.last_motion)
if time_diff > 900 :
    self.turn_off("light.aeotec_zw111_nano_dimmer_level")
    self.notify("Køkken Ø slukket", name = "telegram")
else:
    self.run_in(self.check_now,15 * 60)

I had some issues with the 15 minutes, so I tried to work around that. I don’t know if it is the most elegant solution, but it seems to work :slight_smile:

I am wondering regarding this:

self.last_motion = datetime.datetime.now()

Does this just call a timestamp when called in:

def motion_there(self, kwargs):
self.last_motion = datetime.datetime.now()

Or doesn’t it work that way?

yeah its nothing more then a timestamp.
you just set the var to the actual time at the moment motion is detected.

i mostly use this way. last_changed is also an attribute from sensors, but they were not that reliable in the past (dont know if that is changed) and this way you dont need to translate from str to datetime

the 15 minutes that i did put in was just pseudo code. so you did translate that well :wink:

now you can move your sensors and lights to the yaml and the app can be reused for other motion detectors and light :wink:

1 Like