Stuck with this automation app

I have set up my 1st app to bring the lights on at sunset and that seems to work fine reading from the Lux sensor.

Little stuck with this app I am trying to bring on lights on motion when lux level is under 100…

import appdaemon.plugins.hass.hassapi as hass

class MotionLights(hass.Hass):

    def initialize(self):
        self.listen_state(self.motion, self.args["sensor"])

    def motion(self, entity, attribute, old, new, kwargs):
        if new == "on":
            lux = float(self.get_state("sensor.out_side_lux"))
            light_stat = self.get_state(self.args["light"])
            if lux < 100  and light_stat == "off":
                self.turn_on(self.args["light"])
                self.log("TurnOnLights")
                self.run_in(self.light_off,20)

    def light_off(self, kwargs):
        self.turn_off(self.args["light"])

Any help would be great…

Here is an app I use (originally written by someone else and I’ve made slight modifications) to do this.


import appdaemon.plugins.hass.hassapi as hass


class MotionLightSensor(hass.Hass):
    """Manage a switch using motion and light

    Features:
       - Turns on switch when motion is detected and light level is not above
         configured threshold.
       - Turns off switch after configured delay period
       - If the motion sensor is still active after delay period, keep light on
         and start a new delay period.
       - Optionally, use input_slider to set light threshold and switch adjusts
         directly.

    Arguments:
        light_level:  Above this level, the switch is always off.
        light_slider (optional): Slider to interactively set light_level with.
            The light_level above is used as the starting value.
        delay:  Seconds to keep switch on after binary_sensor has triggered.
        switch:  Switch to control.
        binary_sensor: Motion sensor.
        light_sensor: Sensor used to meassure luminance.

    Example configuration
    In appdaemon.yaml:
        motion_constrained_sensor:
          module: motionConstrainedSensor
          class: MotionLightSensor
          light_level: '10'
          light_slider: input_slider.light_threshold
          delay: '300'
          switch: switch.fibaro_wall_plug_switch
          binary_sensor: binary_sensor.hallway_sensor
          light_sensor: sensor.aeotec_zw100_multisensor_6_luminance

    In configuration.yaml:
        # Define input_slider
        input_slider:
          light_threshold:
            name: Light level treshold
            min: 0
            max: 20
            step: 1
    """

    def initialize(self):
         self.listen_event(self.setup(), "zwave.network_ready") 

    def setup(self):
        self.log("Initializing MotionLightSensor")
        self.handle = None

        # Access all arguments in order to trigger error due to missing
        # arguments during initialization
        self.arg_switch = self.args["switch"]

        self.arg_light_sensor = ""
        if "light_sensor" in self.args:
            self.arg_light_sensor = self.args["light_sensor"]
            # self.log(self.arg_light_sensor)
            # self.log(self.get_state(self.arg_light_sensor))

        self.arg_binary_sensor = self.args["binary_sensor"]

        # Set default threshold
        self.light_threshold = float(100)
        if "light_level" in self.args:
            self.light_threshold = float(self.args["light_level"])

        self.log("Light threshold is {}".format(self.light_threshold))

        # If a slider has been defined, it is used to interactively set light
        # threshold
        if "light_slider" in self.args:
            self.log("Using {} to set light threshold".format(
                self.args["light_slider"]))
            self.set_state(self.args["light_slider"],
                           state=self.light_threshold)
            self.listen_state(self.light_slider_change,
                              self.args["light_slider"])
        self.arg_delay = self.args["delay"]

        # Subscribe to all "on" events from the binary sensor
        self.listen_state(self.motion_event, self.arg_binary_sensor, new="on")

        # Subscribe to all events from the light sensor
        # self.listen_state(self.light_sensor_event, self.arg_light_sensor)

        # Report light sensors current value
        light_level = self.get_light_level()
        self.log("Current light level {}".format(light_level))

    def light_slider_change(self, entity, attribute, old, new, kwargs):
        self.log("Light threshold changed from {} to {}".format(old, new))
        self.light_threshold = float(new)
        light_level = self.get_light_level()
        if (
            light_level > float(new) and
            light_level <= float(old)
        ):
            self.turn_switch_off()
        elif (
            light_level <= float(new) and
            light_level > float(old) and
            self.get_state(self.arg_binary_sensor) == "on"
        ):
            self.turn_switch_on()

    def get_light_level(self):
        if self.arg_light_sensor:
            try:
                light_level = float(self.get_state(self.arg_light_sensor))
                return light_level
            except TypeError:
                return 100.0

        else:
            return 100.0

    def motion_event(self, entity, attribute, old, new, kwargs):
        self.log("Motion detected on {}.".format(entity))
        if self.get_light_level() <= self.light_threshold:
            self.log("It is dark enough")
            self.turn_switch_on()
        else:
            self.log("Light level is currently {} which is above the threshold of {}. Not turning on lights.".format(
                self.get_light_level(), self.light_threshold))

    # def light_sensor_event(self, entity, attribute, old, new, kwargs):
    #     self.log("Light level changed from {} to {}".format(old, new))
    #     if (
    #         float(new) > self.light_threshold and
    #         float(old) <= self.light_threshold
    #     ):
    #         self.turn_switch_off()
    #     elif (
    #         float(new) <= self.light_threshold and
    #         float(old) > self.light_threshold and
    #         self.get_state(self.arg_binary_sensor, "state") == "on"
    #     ):
    #         self.turn_switch_on()

    def start_delay(self):
        self.log("Starting delay timer")
        self.cancel_timer(self.handle)
        self.handle = self.run_in(self.delay_done, self.arg_delay)

    def delay_done(self, kwargs):
        self.log("Delay passed")
        if (self.get_state(self.arg_binary_sensor) == "on"):
            self.log("Motion sensor still active, starting new delay")
            self.start_delay()
        else:
            self.log("Sensor inactive")
            self.turn_switch_off()

    def turn_switch_on(self):
        self.log("Turning on {}".format(self.arg_switch))
        self.turn_on(self.arg_switch)
        self.start_delay()

    def turn_switch_off(self, kwargs=None):
        self.log("Turning off {}".format(self.arg_switch))
        self.turn_off(self.arg_switch)


first i need to mention that when you post code like this it is very hard to read.
on top of the forum is a link that tells you how to present code:

and then you need to be a little more clear.
you are a little stuck doesnt tell us anything about your problem :wink:
the code on its own doesnt look bad, so i dont know where you get stuck.
do you have any errors in your errorlog? if so its very helpfull to present those.

so on its own i think that the only problem you can run into is that lux isnt an int but a string, so it might be that you need to convert that.

Hey Guy’s

Sorry I have only just gotten back to you thanks for the input

I have updated my 1st post to make it easier to read. @kylerw thanks for your code but as this is my 1st APP I thought it would be best to build simple and then dev it more over time to look like yours but thanks…

@ReneTode
I have the App working issue… sometimes it works OK and then other time it switches on the lights and they seem to stay on :confused: as for the Lux information I am getting this reading from a Binary sensor in HA “MQTT” would this still be a string ?

Cheers

Rich

i hope you just mean sensor. a binary sensor has only 2 states on and off or 0 and 1
add some extra logging to see what is going on:

import appdaemon.plugins.hass.hassapi as hass

class MotionLights(hass.Hass):

    def initialize(self):
        self.listen_state(self.motion, self.args["sensor"])

    def motion(self, entity, attribute, old, new, kwargs):
        self.log(new)
        if new == "on":
            lux = float(self.get_state("sensor.out_side_lux"))
            self.log(lux)
            light_stat = self.get_state(self.args["light"])
            if lux < 100  and light_stat == "off":
                self.turn_on(self.args["light"])
                self.log("TurnOnLights")
                self.run_in(self.light_off,20)

    def light_off(self, kwargs):
        self.log("turned_off again")
        self.turn_off(self.args["light"])

and do you really want it to go off after 20 seconds again?

Dammit… :slight_smile: yes it just a sensor, I have added the extra logging and will report back.
20sec was just for testing going to change to to around 5min once i fully understand it all.

Thanks for you help… !!!

In case you’re interested this is my motion_lights originally taken from the teachings of @ReneTode but modified to now keep the light on as long as we have motion. I also use callback constraints in my apps.yaml to instead use the sunrise/sunset times to enable this so its not constantly monitoring something else and to free up threads for other AD things :slight_smile:

import appdaemon.plugins.hass.hassapi as hass

class motion_lights(hass.Hass):


  def initialize(self):
    self.listen_state(self.light_on_motion_function,(self.args["motionID"]))
    self.light_handle = None

  def light_on_motion_function (self, entity, attribute, old, new, kwargs):
    # Lets do a quick check and make sure the light is off
    if new == "active" and old == "inactive":
        if self.get_state(self.args["lightID"]) != "on":
            self.log("Motion detected turn on the lights")
            self.turn_on(self.args["lightID"])
        else:
    # The light is already on so we need to cancle the timer that is already in place and leave the light on until the motion stops
            self.cancel_timer(self.light_handle)
            self.light_handle = None
            self.log("New motion detected leaving the light on for now")
    if new == "inactive" and old == "active" and self.light_handle == None:
        self.log("No more motion turning off the light in 1 min")
        self.light_handle = self.run_in(self.light_off, 60)

  def light_off(self, kwargs):
    # Lets make sure the light is on first just in case
    if self.get_state(self.args["lightID"]) == "on":
        self.log("Turning off the lights now")
        self.turn_off(self.args["lightID"])
        self.light_handle = None

Edit: this is what my apps.yaml looks like for one of my lights

east_side_motion_lights:
  module: motion_lights
  class: motion_lights
  lightID: switch.east_wall_light
  motionID: sensor.east_side_motion
  constrain_start_time: sunset + 00:20:00
  constrain_end_time: sunrise
1 Like