AppDaemon motion detectionlights version 1.1

I have editted the example that Andrew has in Appdaemon.

Added option for several lights, scripts, scenes
Added option for several motiondetectors
Added option to just turn out light after timer ended
Added controle if timer from motionsensor is longer then the delay
Changed handlenaming bug
Changed reset flow

import appdaemon.appapi as appapi
##############################################################################################
# Args:
#
# sensor: binary sensor to use as trigger. several motion detectors are seperated with ,
# entity_on : entity to turn on when detecting motion, can be a light, script, scene or anything else that can be turned on. more lights are sepetated with ,
# 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. more lights are sepetated with ,
# delay: amount of time after turning on to turn off again. If not specified defaults to 60 seconds.
#
# Release Notes
#
# Version 1.1:
#   Added option for several lights, scripts, scenes (Rene Tode)
#   Added option for several motiondetectors (Rene Tode)
#   Added option to just turn out light after timer ended (Rene Tode)
#   Added controle if timer from motionsensor is longer then the delay (Rene Tode)
#   Changed handlenaming bug (Rene Tode)
#   Changed reset flow (Rene Tode)
# Version 1.0:
#   Initial Version (aimc)

class MotionLights(appapi.AppDaemon):

  def initialize(self):
    
    self.handle = None
    
    if "sensor" in self.args:
      self.sensors = self.args["sensor"].split(",")
      for sensor in self.sensors:
        self.listen_state(self.motion, sensor)
    else:
      self.log("No sensor specified, doing nothing")
    
  def motion(self, entity, attribute, old, new, kwargs):
    if "delay" in self.args:
      delay = self.args["delay"]
    else:
      delay = 60
      
    if new == "on":
      if self.handle == None:
        if "entity_on" in self.args:
          on_entities = self.args["entity_on"].split(",")
          for on_entity in on_entities:
            self.turn_on(on_entity)
          self.log("First motion detected: i turned {} on, and did set timer".format(self.args["entity_on"]))
        else:
          self.log("First motion detected: i turned nothing on, but did set timer")          
        self.handle = self.run_in(self.light_off, delay)
      else:
        self.cancel_timer(self.handle)
        self.handle = self.run_in(self.light_off, delay)
        self.log("Motion detected again, i have reset the timer")
  
  def light_off(self, kwargs):
    motion_still_detected = False
    for sensor in self.sensors:
      if self.get_state(sensor) == "on":
        motion_still_detected = True
    if not motion_still_detected:
      self.handle = None
      if "entity_off" in self.args:
        off_entities = self.args["entity_off"].split(",")
        for off_entity in off_entities:
          # If it's a scene we need to turn it on not off
          device, entity = self.split_entity(off_entity)
          if device == "scene":
            self.log("I activated {}".format(off_entity))
            self.turn_on(off_entity)
          else:
            self.log("I turned {} off".format(off_entity))
            self.turn_off(off_entity)
    else:
      self.log("Timer has Ended, but a motion detector is still on. This shouldnt happen, probably is the delay to short.")

I have only tested it for 24 hours, so could be that a revision is coming.
to do list:

  • check if lights are on before motion detection to keep them on.
6 Likes

Hi Rene, I need some help.

I have a running AppDaemon config (two apps running from Andrew) so that part is all fine. I have created a motionlights.py file in my apps directory and copied above into that file.

Then added below to appdaemon.cnf

[Motion Lights]
module = motionlights
class = motionlights

sensor: binary_sensor.fibaro_system_fgms001_motion_sensor_sensor_10_0
entity_on : scene.kitchen_timer
entity_off : scene.kitchen_timer
delay: 300

But I get below error in the error.log

2016-11-23 21:18:36.018188 WARNING ------------------------------------------------------------
2016-11-23 21:19:24.008126 WARNING ------------------------------------------------------------
2016-11-23 21:19:24.008835 WARNING Unexpected error during loading of motionlights.py:
2016-11-23 21:19:24.009157 WARNING ------------------------------------------------------------
2016-11-23 21:19:24.010398 WARNING Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 675, in readApp
    conf.modules[module_name] = importlib.import_module(module_name)
  File "/usr/lib/python3.4/importlib/__init__.py", line 109, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 2254, in _gcd_import
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 1129, in _exec
  File "<frozen importlib._bootstrap>", line 1471, in exec_module
  File "<frozen importlib._bootstrap>", line 321, in _call_with_frames_removed
  File "/home/pi/appdaemon/conf/apps/motionlights.py", line 20, in <module>
    class MotionLights(appapi.AppDaemon):
NameError: name 'appapi' is not defined

2016-11-23 21:19:24.010830 WARNING ------------------------------------------------------------

Any suggestion?

Thanks!

sorry, my bad.
the first 2 lines where not copied when i copied it to the forum.

so past this line:

import appdaemon.appapi as appapi

above your app and it should be fine

(and i have edited the first post for future users)

Ok, that cleared that one . But now below:

2016-11-23 21:19:24.010830 WARNING ------------------------------------------------------------
2016-11-23 21:40:15.010291 WARNING ------------------------------------------------------------
2016-11-23 21:40:15.010626 WARNING Unexpected error during loading of Motion Lights:
2016-11-23 21:40:15.010902 WARNING ------------------------------------------------------------
2016-11-23 21:40:15.011621 WARNING Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 665, in readApp
    importlib.reload(conf.modules[module_name])
KeyError: 'motionlights'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 685, in readApp
    init_object(name, class_name, module_name, config[name])
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 467, in init_object
    APPclass = getattr(module, class_name)
AttributeError: 'module' object has no attribute 'motionlights'

2016-11-23 21:40:15.011957 WARNING ------------------------------------------------------------
2016-11-23 21:40:15.013196 WARNING ------------------------------------------------------------
2016-11-23 21:40:15.013529 WARNING Unexpected error during loading of Motion Lights:
2016-11-23 21:40:15.013829 WARNING ------------------------------------------------------------
2016-11-23 21:40:15.014330 WARNING Traceback (most recent call last):
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 685, in readApp
    init_object(name, class_name, module_name, config[name])
  File "/usr/local/lib/python3.4/dist-packages/appdaemon/appdaemon.py", line 467, in init_object
    APPclass = getattr(module, class_name)
AttributeError: 'module' object has no attribute 'motionlights'

2016-11-23 21:40:15.014634 WARNING ------------------------------------------------------------

Update: Solved; this needed to be MotionLights (capital M & L). Running ok!

p.s. Would it be possible to add an extra ‘parameter’ for elevation to the config? Actually only want the automation to work when below is true (in this case):

 entity_id: sun.sun
    value_template: '{{ state.attributes.elevation }}'
    below: 3.5

If I want this app to work in a different room; would I rename the app itself or would I create only multiple entries in the appdaemon.cnf?

you can use constraints in your cfg file.

like:

constrain_start_time = sunset - 00:45:00
constrain_end_time = sunrise + 00:45:00

if you realy want to use elevation you can make an input_boolean in HA which is on at the desired time and then use:

constrain_input_boolean = input_boolean.your_boolean

and yeah, you can reuse this app as often as you like. thats the big advantage from appdaemon.
so just create a new block in your cfg file with a different name and you can use different args, so different rooms.

Nice! Thanks!

Sorry, more Q’s: is the “delay” seconds or minutes? I now have delay = 300 but does not switch off (expecting 300 sec).

I have an scene that turns two lights on and a scene that turns these same lights off.

[MotionLights]
module = motionlights
class = MotionLights

constrain_start_time = sunset - 00:45:00
constrain_end_time = sunrise + 00:45:00 
sensor: binary_sensor.fibaro_system_fgms001_motion_sensor_sensor_10_0
entity_on : scene.kitchen_timer
entity_off : scene.kitchen_timer_off
delay: 300

delay is seconds.
so it should turn off after 5 mins.
if it detects a second movement you should see that in your log or on screen.
and also after the timer ends you should get an entry in your log or on screen.

your config looks allright.

Oops, my bad! I did not reload HASS when I created the _off scene… All working as expected.

1 Like

i added an off function to your version of motion_lights.py to reset the timer, when at least one entity is turned off manually. if this functionalty is missing, an entity that is being turned off in HA, won’t turn on when motion is detected, as long as the timer is still running.

1 Like

i dont need it because my timer is set to 60 or 300 seconds, but nice.
it could even be better if the off switches would be defined seperatedly.

so that you could turn of the motion lights with any switch you set in the args.

A quick question

You have written:
def off(self, entity, attribute, old, new, kwargs):
if self.get_state(entity) == “off”:

wouldn’t it be more streamlined if you just do
if new == “off”:

And how do I make a post where I can paste code in a proper way? Bugs me that I can’t figure it out

/R

A question Rene:

Why do you specifically want new == on when setting up the timer? Wouldn’t it be better to allow for off to also trigger the timer? Then I think you could clean up your light_off to not need to look for motion still detected right?

/R

about your question to post code:

to post code correctly you need ` 3 times and then a new line. before and after the code.
not ’ and not " (sorry dont know the names from those symbols) the one you use to create è.

your question to diplix is right. that would be the way.

why do i want new= on?
i know that there are motion detectors which have there own timer. they keep on for the time they are set to, for instance between 5 an 30 seconds and you can set that with a potentiometer.
but my code has no way to detect that. so your delay would be affected by that.

the second thing you say is that you dont need to look after the motion detection is set to off.
and thats also not true. if you have an motion detector which goes to off after 5 seconds then you need to stay reel busy to make sure it still keeps detecting you.

suppose you use it for a bathroom. you want the light to stay on while you are on the toilet.
you dont move very much. lets say you move not at all for 60 seconds. allmost every motion detector would have gone to off by that time. but you still like the lights to stay on, untill your gone.

so thats why i think you need to keep checking untill the end off the delay. and only for movement.
thats also why the last line checks if the motion detector is still set to on at the end off the timer.

to use motion detection like this you want your motiondetector settings to be as short as possible.

Ok. Will try that out for posting code. Stay tuned! :wink:

Regarding what I meant with off is that new == off should also trigger the timer, not turn off the lights. Because then you will cover more more scenarios. E.g. When you move detection will go to on and timer will set but only at the beginning of movement. If the detection stays hi for longer than 60 seconds, the lights will turn off. But if you also care about the signal for new == off as in no motion detected and then also set the timer for countdown you will be less likely to actually need to check if sensors are still active.

Am I explaining it well enough or should I write a logical example of what I mean? :slight_smile:

/R

i think your logic is wrong.
actually the best motion detector you would have goes off diectly without any delay after the motion is detected.
but lets say you have 1 that stays high for 4 minutes.
the motion is detected and your timer is set to 5 mins. (bacause you like it to stay on for 5 mins after detection)
if your timer is influenced by your detector going to off, you lights would stay on for 9 minutes total.

going off is saying nothing about the motion, because of the fact that every motion detector is different.
it can say that there was no motion for some time (if you have a motion detector which stays high for repeated motion) but it can also say that just some time has passed since the first motion is detected.
and the some in some time is also unknown.

i set my detectors as low as possible. so it wont stay high for 60 seconds.
if you have a detector which stays high as long as it detects movement (and also has its own delay) you actually dont need an app like this. they you just would like your lights to go off when the detector goes to off.

so there is a hardware difference. some do have a decent program like this included in the hardware and some dont.
with this app you like the once that dont have any delay.

I see! I understand better now. Thanks for explaining.

It sounds like I need to investigate motion detection behaviour prior to buying any myself…

I’ll experiment with this when I get myself som motion detectors :slight_smile:

/R

i use cheap motion detectors that i can connect to my arduinos.
i could also use arduino programming to set my delay. but if it is in the hardware or the arduino is the same.
i dont want that because i like to be able to change settings from my pc and not on my hardware :wink:

good luck with finding out what is right for you.

@ReneTode

Haven’t changed this for a long time but now I want to differentiate the same light for evening use and night use. With this I mean that, when after 0:00 until sunrise, I want the motionlight still to work but with a dimmed light. What is the easiest way to achieve this? Was think of just duplicating the current one and then use times that are adjacent and refer to different scene. Code below. Is that the easiest way and would that work?

My apps.yaml would then contain:

MotionLights:
  module: motionlights
  class: MotionLights
  constrain_start_time: sunset - 00:45:00
  constrain_end_time: 0:00
  sensor: binary_sensor.fibaro_system_fgms001_motion_sensor_sensor_10_0
  entity_on: scene.kitchen_timer
  entity_off: scene.kitchen_timer_off
  delay: 600

MotionLights:
  module: motionlights
  class: MotionLights
  constrain_start_time: 0:00
  constrain_end_time: sunrise + 00:45:00 
  sensor: binary_sensor.fibaro_system_fgms001_motion_sensor_sensor_10_0
  entity_on: scene.kitchen_timer_night
  entity_off: scene.kitchen_timer_nigh_off
  delay: 200