Automation - dark house turn on lights - not working

Could someone figure out what I am missing in the following code.

I converted my existing working automation and used example code for reference.

When dark sky cloud coverage (sensor) is more than 70 (as parameter) then turn on the lights (group.weather_related_switches) and run this from sunrise to two hours before sunset

apps.yaml:

Dark House:
  class: DarkHouse
  module: dark_house
  #only called when the input_boolean is OFF
  constrain_input_boolean: input_boolean.is_it_cloudy_with_chance_of_meatballs,off
  entity_off: group.weather_related_switches
  entity_on: group.weather_related_switches
  start_time: sunrise
  end_time: sunset - 02:00:01
  sensor: sensor.dark_sky_cloud_coverage
  skycoverage: 70

dark_house.py:

import appdaemon.appapi as appapi
import datetime

class DarkHouse(appapi.AppDaemon):

  def initialize(self):
    
    self.active = False
    self.set_state("input_boolean.is_it_cloudy_with_chance_of_meatballs", state = 0)
    self.log(str(self.get_scheduler_entries()))

    darkskycloud = self.get_state(self.args["sensor"])
    
    # Arguments:  sensor.dark_sky_cloud_coverage - Value : 86.0
    self.log("Arguments:  {}".format(self.args["sensor"]) + " - Value : " + darkskycloud)

    # Ex: Parameter : 70
    self.log("Parameter : {}".format(self.args["skycoverage"]))

    self.listen_state(self.light_event, self.args["sensor"])

  def light_event(self, entity, attribute, old, new, kwargs):

    self.log("Inside light_event function")
    darkskycloud = self.get_state(self.args["sensor"])

    self.log("Updated Sensor reading from : {}".format(self.args["sensor"]) + " - Value : " + darkskycloud)

    self.log("Low light detected: turning {} ON".format(self.args["entity_on"]))

    if self.now_is_between(self.args["start_time"], self.args["end_time"]):
      if darkskycloud > int(self.args["skycoverage"]) and not self.active:
        self.active = True
        self.set_state("sensor.is_it_cloudy_with_chance_of_meatballs", state = 1)
        if "entity_on" in self.args:
          self.log("Turning {} on".format(self.args["entity_on"]))
          self.turn_on(self.args["entity_on"])
          msg="Attention: \nIt is getting a little dark inside the house. \nI will turn on some extra lights in the living room and kitchen. \n"
          self.send_notification_msg(msg)

      if darkskycloud < int(self.args["skycoverage"]) and self.active:
        self.active = False
        self.set_state("sensor.is_it_cloudy_with_chance_of_meatballs", state = 0)
        if "entity_off" in self.args:
          self.log("Turning {} off".format(self.args["entity_off"]))
          self.turn_off(self.args["entity_off"])
          msg="Attention: \n I will turn off lights in the living room and kitchen"
          self.send_notification_msg(msg)
    else:
      # We are now dormant so set self.active false
      self.active = False
      self.set_state("sensor.is_it_cloudy_with_chance_of_meatballs", state = 0)
      msg="Attention: This code is working. Please test this later"
      self.send_notification_msg(msg)

  def send_notification_msg(self, msg):
      self.call_service('ifttt/trigger', event='ShreRam_Home', value1=msg)
      self.call_service('tts/google_say', entity_id='media_player.living_room_home', message=msg)

Log file when I run appdaemon from command line :

pi@rpi3-aio:/home/homeassistant/.homeassistant/config/logs $ appdaemon -c /home/homeassistant/.homeassistant/config
2017-10-06 09:44:11.455468 INFO AppDaemon Version 2.1.9 starting
2017-10-06 09:44:11.455997 INFO Configuration read from: /home/homeassistant/.homeassistant/config/appdaemon.yaml
2017-10-06 09:44:11.862878 INFO Starting Apps
2017-10-06 09:44:12.290821 INFO Got initial state
2017-10-06 09:44:12.293046 INFO Loading Module: /home/homeassistant/.homeassistant/config/apps/hello.py
2017-10-06 09:44:12.294753 INFO Loading Object hello_world using class HelloWorld from module hello
2017-10-06 09:44:12.453059 INFO hello_world: Hello from AppDaemon
2017-10-06 09:44:12.455820 INFO hello_world: You are now ready to run Apps!
2017-10-06 09:44:12.456297 INFO Loading Module: /home/homeassistant/.homeassistant/config/apps/utils.py
2017-10-06 09:44:12.458020 INFO Loading Object utils using class utilities from module utils
2017-10-06 09:44:12.458718 INFO Loading Module: /home/homeassistant/.homeassistant/config/apps/sunsetlights.py
2017-10-06 09:44:12.460202 INFO Loading Object sunset_lights using class TurnOnLightsAtSunset from module sunsetlights
2017-10-06 09:44:12.467709 INFO sunset_lights: Next sunset at 2017-10-06 18:38:01
2017-10-06 09:44:12.470453 INFO sunset_lights: {}
2017-10-06 09:44:12.471322 INFO Loading Module: /home/homeassistant/.homeassistant/config/apps/bedtimelights.py
2017-10-06 09:44:12.474266 INFO Loading Object bedtime_lights using class TurnOffLightsAtBedtime from module bedtimelights
2017-10-06 09:44:12.481828 INFO bedtime_lights: Today date: 2017-10-06 - Time to trigger: 23:35:00
2017-10-06 09:44:12.482808 INFO Loading Module: /home/homeassistant/.homeassistant/config/apps/dark_house.py
2017-10-06 09:44:12.484493 INFO Loading Object Dark House using class DarkHouse from module dark_house
2017-10-06 09:44:12.703893 INFO Dark House: {'bedtime_lights': {UUID('561c31ba-b438-49e7-a309-0f21d4b27055'): {'type': None, 'kwargs': 1507350900, 'repeat': 1507350900, 'callback': <bound method TurnOffLightsAtBedtime.run_daily_callback of <bedtimelights.TurnOffLightsAtBedtime object at 0x7590da50>>, 'basetime': 1507350900, 'name': 'bedtime_lights', 'offset': 1507350900, 'interval': 1507350900, 'timestamp': 1507350900}}, 'sunset_lights': {UUID('86bcee05-3553-44a7-9e66-3ed99066ac0a'): {'type': 'next_setting', 'kwargs': 1507333081, 'repeat': 1507333081, 'callback': <bound method TurnOnLightsAtSunset.sunset_cb of <sunsetlights.TurnOnLightsAtSunset object at 0x758ddf70>>, 'basetime': 1507333081, 'name': 'sunset_lights', 'offset': 1507333081, 'interval': 1507333081, 'timestamp': 1507333681}}}
2017-10-06 09:44:12.706841 INFO Dark House: Arguments:  sensor.dark_sky_cloud_coverage - Value : 84.0
2017-10-06 09:44:12.709485 INFO Dark House: Parameter : 70
2017-10-06 09:44:12.710105 INFO App initialization complete
2017-10-06 09:44:12.710764 INFO Dashboards are disabled
2017-10-06 09:44:12.711060 INFO API is disabled
2017-10-06 09:44:12.894201 INFO Connected to Home Assistant 0.54.0

i see a couple of things:

  1. input_boolean state is "on"or “off” not 0 or 1
  2. you dont use set_state for input_boolean but turn_on or turn_off
  3. you dont want to manipulate the state from the constraint within the app (constraint says that the app is running or not (if the boolean is off, the app is not running, so no state changes from the sensor are registered))
  4. you only get to the code if there is a change in the state from the sensor. so if the lightlevel wont change you would never reach the dorment period.

in stead off using the start time and end time you could better use constraints for start and end time.
you are also combining ways to log.
you better chose 1 or the other so or:

self.log("Arguments:  " + self.args["sensor"] + " - Value : " + darkskycloud)

or

self.log("Arguments:  {} - Value: {}".format(self.args["sensor"],darkskycloud))

Thank you.

I am new to Python and Appdaemon.

All I was trying to do was turn on a couple of switches (zwave) when the sky is cloudy (via dark sky cloud coverage).

Already that input_boolean is used to in the code so I thought I could reuse that in appdaemon app. I wanted to run this new app only in the case that value is OFF.

constrain_input_boolean: input_boolean.is_it_cloudy_with_chance_of_meatballs,off

I guess I have to find another constraint instead of input_boolean that I was using.

I wanted to run this app only between sunrise and sunset (with offset). Could you explain bit more?

Thank for the tip. I adjusted the logic like this.

#self.set_state(“input_boolean.is_it_cloudy_with_chance_of_meatballs”, state = 0)
self.turn_off(“input_boolean.is_it_cloudy_with_chance_of_meatballs”)

Yeah. That’s what happening. Am I assuming whenever Darksky sensor (cloud_coverage) value updates then this function will be called, correct?

self.listen_state(self.light_event, self.args["sensor"])

i think you need to read the api a few times
http://appdaemon.readthedocs.io/en/latest/APIREFERENCE.html#
but for sure part from this will help you:
http://appdaemon.readthedocs.io/en/latest/APPGUIDE.html
in particular the parts about constraints.

a constraint is when the app is running or not. so the boolean is_it_cloudy_with_chance_of_meatballs is used as an constraint. (see your apps.yaml)
so you DONT want to manipulate that in your app.
it is like do i want this app to be active or not!
if you set it to inactive in the initialize, it will never work.

what you would want to do:

you would like to switch on a couple off switches when a sensor reaches a value:
so like:

init():
  listen_state(self.callback, yoursensor)
self.callback():
  self.turn_on(some_switch)

you want to do it between some times, so set constaints in apps.yaml

keep your first steps as simple as possible to be sure you understand what is happening.

start with things like:

import appdaemon.appapi as appapi
import datetime

class DarkHouse(appapi.AppDaemon):

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

  def light_event(self, entity, attribute, old, new, kwargs):
    self.log(entity + " has changed to: " + str(new))

if you have that working go to:

import appdaemon.appapi as appapi
import datetime

class DarkHouse(appapi.AppDaemon):

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

  def light_event(self, entity, attribute, old, new, kwargs):
    self.log(entity + " has changed to: " + str(new))
    self.turn_on(any_entity)

then try to go on from that step by step.
like:

  if entity > any_value:
    self.turn_on(any_entity)

if you try to learn everything at once, you wont know what you are doing wrong.

1 Like

Thank you for the detailed reply. Finally, I was able to resolve issues around turning lights when it’s dark in the house automation.

Converting automation yaml to python apps is much better.

I am going to use your suggestion and start gradually and add later add bells and whistles.

2 Likes

Btw, is there any way to display toggle for appdaemon apps on UI? I display existing automations and can disable them when I want it. I didn’t install HADashboard. I am currently running HA AIO on Pi in the virtual environment.

Ex:

image

For now, I added input_boolean (dark_house_lights) for the appdaemon app and displayed like this on UI.

image

Dark House:
  class: DarkHouse
  module: dark_house
  #only called when the input_boolean is turned ON in the UI
  constrain_input_boolean: input_boolean.dark_house_lights,on
  entity_off: group.weather_related_switches
  entity_on: group.weather_related_switches
  start_time: sunrise
  end_time: sunset - 00:30:00
  sensor: sensor.dark_sky_cloud_coverage
  skycoverage: 70

Anyone from this forum active on Discord #ecosystem channel?

that is exactly the way to go. thats where the constaint_input_boolean is meant for.
i am on discord, but never read in ecosystem, or HA groups.

I successfully converted my automations to appdaemon within a week and same took me a couple of months to work with yaml.:open_mouth:

Is there any section in the forums to share the appdaemon code? Most of the shared code under ‘Shared Projects’ were related to HA components/yaml stuff but not specific to ‘appdaemon’.

There is a cookbook section but most of the stuff was focused on ‘yaml’. Is there any other place that I forgot to look?

Kudos to developer @aimc :sunglasses: and too @ReneTode / @SupahNoob for patiently replying to my queries.

1 Like

in the appdaemon section from the forum(where you are now) you can share your apps.
but if you are looking for things from others you have to search. (there is not much)

there are ideas for a app section, but that isnt worked out.

Today for some reason this automation didn’t kickoff. I am using ‘sensor.dark_sky_cloud_coverage’ to turn on lights inside the house. If the number is greater than 70 then I added steps to turn on few lights.

But from morning cloud coverage was 100%

sensor.dark_sky_cloud_coverage 100
unit_of_measurement: %

I have following function to be called inside initialize whenever sensor value changes. But that function never was called as cloud coverage was 100% throughout the day.

self.listen_state(self.new_light_event, self.args["sensor"])

How do I force AD to use alternate logic to call a different function if the cloud coverage is 100% (not updated at all during that day)?

Here is the entire function that gets called whenever sensor value changes.

`  def new_light_event(self, entity, attribute, old, new, kwargs):`

    # self.log('DarkHouse: Callback Function triggered : ')
    # self.utils = self.get_app('utils')

    # self.log("Entity name : {} - Old : {} - New - {}".format(entity, old, new))

    darkskycloud = self.get_state(self.args["sensor"])

    self.log("Updated Sensor reading from : {}".format(self.args["sensor"]) + " - Value : " + darkskycloud)

    msg = "Attention: "
    if self.now_is_between(self.args["start_time"], self.args["end_time"]):
      # added additional condition just in case if darksky fails to send valid value
      if darkskycloud != "unknown":
        sensorvalue =  float(self.get_state(self.args["sensor"]))
        if int(sensorvalue) > self.args["skycoverage"] and not self.active:
          self.active = True
          self.turn_on("input_boolean.is_it_cloudy_with_chance_of_meatballs")

          msg += "It is getting a little dark inside the house. "

          self.utils.turn_on_off_handler(msg, 'on', self.args["entities"])

        elif int(sensorvalue) < self.args["skycoverage"] and not self.active:

          if self.get_state("input_boolean.is_it_cloudy_with_chance_of_meatballs").lower() == 'on':
            self.log("go further..lights were turned on..go for turnoff logic")
            self.active = False
            self.turn_off("input_boolean.is_it_cloudy_with_chance_of_meatballs")
            msg += "Cloudy weather cleared. "
            self.utils.turn_on_off_handler(msg, 'off', self.args["entities"])
          else:
            self.log("ignore..lights were not turned on")
        else:
          self.active = False
      else:
        self.active = False
        self.log("Something is not right. Check the logic. Sensor : {} - Value : {}".format(self.args["sensor"],self.get_state(self.args["sensor"])))
    else:
      self.active = False
      self.log("Outside - START : {} time and END : {} time. Now : {} ".format(str(self.sunrise()), str(self.sunset()), datetime.datetime.fromtimestamp(appdaemon.conf.now)))

Here is snippet from apps.yaml

Dark House:
  class: DarkHouse
  module: dark_house
  # this gets called only when this input_boolean is turned ON from the HA user interface
  constrain_input_boolean: input_boolean.ad_dark_house_lights
  sensor: sensor.dark_sky_cloud_coverage
  # following are parameters
  entities:
    - switch.ge_12722_onoff_relay_switch_switch_2
    - switch.leviton_unknown_type1c02_id0334_switch
    - light.hallway_light_bulb_two
  start_time: sunrise
  # added offset so that it won't mess with other sunset_lights_1 start time
  end_time: sunset - 01:14:00
  skycoverage: 70
  constrain_presence: anyone

listen_state only gets activated with a state change.
if the state isnt changed all day long it wnt be activated.
if you want the function still to be triggered at the start time, you need to add a run_daily with a time just after your start time.

but then you get the problem that you cant call the same callback because the need a different amount of arguments.
that can be solved by letting both callbacks trigger a third function, in which you place all your code.
but then you wont have access to old, new and attributes anymore.
you can give those along as kwargs to the new function.

hope this helps you.