Switching off lights when people leave the house

So I started using AppDaemon (version 2.1.10) to automate things, but I have a hard time figuring out the correct syntax.

What I want:

  • if we are not at home
  • if the sun is up
  • if the lights are on
  • if the override button is off
  • then switch off all lights.

How I implemented this:

  • event listeners for specific devices leaving the house (bluetooth and gps)
  • event listener for the sun <-- this is problematic

Either the sun listener keeps every minute shouting that it is above the horizon or I get some positional arguments error. Do I have the correct approach here?

My code:

import appdaemon.appapi as appapi
#
# Automation 5 - Away for the day. 
#
# Switch off all the lights
#
class Auto5(appapi.AppDaemon):

 def initialize(self):
   self.log("Automation 5 reloaded. Waiting for people leaving the house.")
   self.listen_state(self.check_away, "device_tracker.user2_iphone",    old="home")
   self.listen_state(self.check_away, "device_tracker.user1_bluetooth", old="home")
   self.listen_state(self.check_away, "device_tracker.user1_iphone",    old="home")
   
   # this keeps on firing each minute
   # self.listen_state(self.check_away, "sun.sun", new="above_horizon")
   
   # this raises an error:
   # TypeError: check_away() missing 4 required positional arguments: 'attribute', 'old', 'new', and 'kwargs'
   # self.run_at_sunrise(self.check_away)
     
 def check_away(self, entity, attribute, old, new, kwargs):

   self.log("A device left home, checking for other conditions")
   
   # check actual state of devices and time conditions 
   if (self.get_tracker_state("device_tracker.user2_iphone") != "home"
     and self.get_tracker_state("device_tracker.user1_bluetooth") != "home"
     and self.get_tracker_state("device_tracker.user1_iphone") != "home"
     and self.get_tracker_state("input_boolean.override") == "off"
     and self.now_is_between("sunrise", "sunset")):
 
     self.log("Everyone is away for the day, and there is no override. Switching off all lights.")
     self.call_service("light/turn_off", 
       entity_id = "group.all_lights") 
       
     # and send a message 
     self.call_service("notify/ios_iphone_1", 
       message = "All lights have been switched off") 

   
  

you listen if the old state from your devices is home and then you trigger the callback.
so if your phone is home the callback will be called every time its updated in HA.

i didnt even know there was a get_tracker_state but it seems to do the same as get_state.

to make sure that you get the right things in your if, just test it before with something like:

self.log("state from device user2_iphone: " + self.get_state(“device_tracker.user2_iphone”))

i should say that your code looks right, so i have no clue why things go wrong.
most likely it is your now_is_between that is causing trouble.
you could change that with the sun_up() function

You shouldn’t use the sun as a trigger - just check if it is up or down when you get a trigger from the device tracker, using the in built functions. The state of the sun is updated frequently as you have seen so it is not useful as a trigger normally.

look at his code Andrew, he doesnt have the sun as trigger.

and i dont see why his if statement should set off anything unless no device is mentioned as home and the sun is up.
but that could be a devicetracking problem.
its even in the hass docs to set the time for changing not to low, because there are issues with devices giving the wrong status.

i for myself have given up to use phones for automations when people are home.
i still havent found a dependable way to see if a mobile is at home or not.

Yeah I know - I was just responding in general because he was talking about listening to the sun state which isn’t often a useful thing to do.

1 Like

Looking at the code - first, yes the approach does look correct, but the problem I see is that you are trying to turn a group off with the light/turn_off service - I’m not sure if that will work. Try group/turn_off or homeassistant/turn_off instead.

i didnt look at that part. but thats a valid point.

reading back i see that his questions and his code are not the same, you reacted to his text and i to his code.
guess i have to work on my reading skills again :wink:

but i think together we have pointed out enough for him to go in the right direction :wink:

1 Like

The code runs fine at the moment, but I cannot use the sun as trigger.

The point is that if all our devices are already away before sunrise, there will be no trigger when the sun rises above the horizon, and therefore the lights remain on.

The action part works fine, all lights get switched off properly.

I hope I made things more clear, I will investigate further. Thanks for the responses so far :slight_smile:

That’s what run_at_sunrise() is for. It will create a callback at sunrise.

I fixed it by moving the sunrise callback and the logic to a separate function. I still had a hard time with the proper syntax, resulting in errors like

TypeError: check_away() missing 4 required positional arguments: 'attribute', 'old', 'new', and 'kwargs'
TypeError: run_at_sunrise() takes 2 positional arguments but 5 were given

Even the official home assistant documentation is not correct here, the example with Sunrise/Sunset Lighting cannot be run without errors :frowning:

Anyway, my code now runs fine, assuming that self.listen_state(self.check_away, "device_tracker.user2_iphone", old="home") means ‘run function check_away as soon as the state of the device changes and the old state=home’.

import appdaemon.appapi as appapi
#
# Automation 5 - Away for the day. 
#
# Switch off all the lights
#
class Auto5(appapi.AppDaemon):

  def initialize(self):
    self.log("Automation 5 reloaded. Waiting for sunrise or people leaving the house.")
    # listen for devices leaving the house.
    self.listen_state(self.check_away, "device_tracker.user2_iphone",    old="home")
    self.listen_state(self.check_away, "device_tracker.user1_bluetooth", old="home")
    self.listen_state(self.check_away, "device_tracker.user1_iphone",    old="home")

    # listen for sunrise  
    self.run_at_sunrise(self.sunrise_cb)
    
  def sunrise_cb(self, kwargs):
   self.log("Sunrise detected")
   self.logic()
   
  def check_away(self, entity, attribute, old, new, kwargs):
    self.log("A device left home, checking for other conditions")
    self.log("Entity: " + entity)
    self.log("Old: " + old)
    self.log("New: " + new)
    self.logic()    
    
  def logic(self):
    self.log("Checking for other conditions")  
    
    # check actual state of devices and time conditions 
    if (self.get_tracker_state("device_tracker.user2_iphone")      != "home"
      and self.get_tracker_state("device_tracker.user1_bluetooth") != "home"
      and self.get_tracker_state("device_tracker.user1_iphone")    != "home"
      and self.get_tracker_state("input_boolean.override")         == "off"
      and self.now_is_between("sunrise", "sunset")):
  
      self.log("Everyone is away for the day, and there is no override. Switching off all lights.")
      self.call_service("light/turn_off", 
        entity_id = "group.all_lights") 
        
      # and send a message 
      self.call_service("notify/ios_iphone_1", 
        message = "All lights have been switched off")   
    else:
     self.log("The lights will not be switched off")  

Tested it with the time machine:

sudo appdaemon -s "2017-10-01 07:00:00" -e "2017-10-01 09:00:00" -c . -t 0.1

what do you expect?
2 seperate parties, both not professional, without any profit, working separatly on the same goal, without communication or connection on direction or whatsoever (remember 2 seperate parties)
do you expect both of them to have up to date docs about the other party?

like i said it means it will run everytime that the devicetracker is updated and old state is home.
so it means that if you have your devicetracker check your state every 10 seconds, it will trigger the callback every 10 secons untill something changes.
you can better use new instead of old, because it would trigger the callback less.

I can help: Fixed the sunrise / sunset example code in the documentation by johanvanderkuijl · Pull Request #3809 · home-assistant/home-assistant.io · GitHub

1 Like

offset should work but then like:
self.run_at_sunrise(self.sun, offset = 30 * 60, “Sunrise +30 mins”)