[SOLVED] Use a script with times optional

So here is today’s scripting attempt as you can tell I’m getting a little more brave now trying to combine 2 things into one script.
Here is the use case. Use it for both scheduled times as well as presence

Here is my appdaemon.yaml

water_heater_off:
  module: turn_on_off_at_time
  class: daily_turn_on_off_time
  timeon: "04:30:00"
  timeoff: "00:00:00"
  switchID: switch.water_heater_switch
  constrain_presence: anyone

water_heater_off_nobody_home:
  module: turn_on_off_at_time
  class: daily_turn_on_off_time
  switchID: switch.water_heater_switch

The app itself

import appdaemon.appapi as appapi
import datetime

class daily_turn_on_off_time(appapi.AppDaemon):
#
#
#
# Args
# timeon = what time you want this to turn on
# timeoff = what time you want this to turn off
# switchID = what do you want to turn on/off
#

  def initialize(self):
# So first we'll take the time that is passed into us and parse it out
    timeon = self.args["timeon"]
    timeoff = self.args["timeoff"]
    if timeon is not None:
      time_on_parsed = self.parse_time(timeon)
    if timeoff is not None:
      time_off_parsed = self.parse_time(timeoff)


# Now its time for the fun stuff
    if time_on_parsed is not None:
      self.run_daily(self.turn_on_function, time_on_parsed)
    if time_off_parsed is not None:
      self.run_daily(self.turn_off_function, time_off_parsed)
    self.listen_state(self.turn_off_function, "group.all_devices", new="not_home")



  def turn_on_function(self, entity, attribute, old, new, kwargs):
    self.turn_on(self.args["switchID"])

  def turn_off_function(self, entity, attribute, old, new, kwargs):
    self.turn_off(self.args["switchID"])

Problem is this

2018-03-06 15:08:48.029320 WARNING Unexpected error during loading of water_heater_off_nobody_home:
2018-03-06 15:08:48.030129 WARNING ------------------------------------------------------------
2018-03-06 15:08:48.032972 WARNING Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/appdaemon/appdaemon.py", line 921, in read_app
    init_object(name, class_name, module_name, config[name])
  File "/usr/lib/python3.6/site-packages/appdaemon/appdaemon.py", line 595, in init_object
    conf.objects[name]["object"].initialize()
  File "/config/hadaemon/apps/turn_on_off_at_time.py", line 16, in initialize
    timeon = self.args["timeon"]
KeyError: 'timeon'

2018-03-06 15:08:48.033943 WARNING ------------------------------------------------------------

I was thinking that I"ll just check to make sure the value isn’t null before we proceed but I guess I just don’t understand how to properly do that. I tried also doing

if self.args[“timeon”] is not None:

but even that has issues so I think that I just don’t know how to properly do that.

in your second app configuration, you do not have a timeon variable so it can’t grab it. You’ll want to include a check to make sure it exists or add it to the app config.

This community here is just awesome. Greatly appreciate your help @kylerw I will add some logic now to hopefully just do it all up

EDIT: Before I go and try it is there anything wrong with

if self.args[“timeon”] is not None:

I’m not certain, but that may also throw the error because you are pulling the value to compare to None. YOu may need a try/except.

        try:
            timeon = self.args[“timeon”]
        except KeyError:
            self.error("You must supply a valid timeon!")
            ### Or simply ignore the variable assigment
            return
1 Like

I’ve also seen

if "timeon" in self.args: used

1 Like

so if I understand this correctly this

        try:
            timeon = self.args[“timeon”]
        except KeyError:
            self.error("You must supply a valid timeon!")
            ### Or simply ignore the variable assigment
            return

its going to just check to see if there is something there and if there is its like hey this is cool if not it tosses out an error and moves on?

and today’s miracle worker is @kylerw

This works like a champ

import appdaemon.appapi as appapi
import datetime

class daily_turn_on_off_time(appapi.AppDaemon):
#
#
#
# Args
# timeon = what time you want this to turn on (optional)
# timeoff = what time you want this to turn off (optional)
# switchID = what do you want to turn on/off
#

  def initialize(self):
# So first we'll take the time that is passed into us and parse it out
    try: 
       timeon = self.args["timeon"]
    except KeyError:
        return

    try:
       timeoff = self.args["timeoff"]
    except KeyError:
        return

    if timeon is not None:
      time_on_parsed = self.parse_time(timeon)
      self.run_daily(self.turn_on_function, time_on_parsed)

    if timeoff is not None:
      time_off_parsed = self.parse_time(timeoff)
      self.run_daily(self.turn_off_function, time_off_parsed)

    self.listen_state(self.turn_off_function, "group.all_devices", new="not_home")


# Now its time for the fun stuff
  def turn_on_function(self, entity, attribute, old, new, kwargs):
    self.turn_on(self.args["switchID"])

  def turn_off_function(self, entity, attribute, old, new, kwargs):
    self.turn_off(self.args["switchID"])

I wanted to be able to use this same functionality in two diff scenarios without re writing the whole thing. So now I can do that.

We either run this at a set time every day or when everyone leaves the house. In my use case it was for my water heater. I turn it off every night at midnight and then back on at 430 for showers then back off when we all leave for the day.

you can delete all this:

    try: 
       timeon = self.args["timeon"]
    except KeyError:
        return

    try:
       timeoff = self.args["timeoff"]
    except KeyError:
        return

if you change to this:

    if "timeon" in self.args:
      time_on_parsed = self.parse_time(self.args["timeon"])
      self.run_daily(self.turn_on_function, time_on_parsed)

but you have another problem there:

self.run_daily(self.turn_off_function, time_off_parsed)

needs a callback that looks like this:

  def turn_on_function(self, kwargs):

and not like this:

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

or it will generate errors and wont work
the listen_state needs that kind off callback though.

so you cant use 1 callback for both listen_state and run_daily
you can use an in between function but because your functions are so simple i would go with:

  def turn_on_function1(self, entity, attribute, old, new, kwargs):
    self.turn_on(self.args["switchID"])

  def turn_off_function1(self, entity, attribute, old, new, kwargs):
    self.turn_off(self.args["switchID"])

  def turn_off_function2(self, kwargs):
    self.turn_off(self.args["switchID"])

  def turn_on_function2(self, kwargs):
    self.turn_on(self.args["switchID"])

1 Like

So in the end it should all look like

import appdaemon.appapi as appapi
import datetime

class daily_turn_on_off_time(appapi.AppDaemon):
#
#
#
# Args
# timeon = what time you want this to turn on (optional)
# timeoff = what time you want this to turn off (optional)
# switchID = what do you want to turn on/off
#

  def initialize(self):
# So first we'll take the time that is passed into us and parse it out
    if "timeon" in self.args:
      time_on_parsed = self.parse_time(timeon)
      self.run_daily(self.turn_on_function2, time_on_parsed)

    if "timeoff" in self.args:
      time_off_parsed = self.parse_time(timeoff)
      self.run_daily(self.turn_off_function2, time_off_parsed)

    self.listen_state(self.turn_off_function1, "group.all_devices", new="not_home")


# Now its time for the fun stuff
  def turn_on_function1(self, entity, attribute, old, new, kwargs):
    self.turn_on(self.args["switchID"])

  def turn_off_function1(self, entity, attribute, old, new, kwargs):
    self.turn_off(self.args["switchID"])

  def turn_off_function2(self, kwargs):
    self.turn_off(self.args["switchID"])

  def turn_on_function2(self, kwargs):
    self.turn_on(self.args["switchID"])

yes. great.
and if you ever come to the point where you want the turn off and on functions to do a whole lot more, then you can start of thinking about creating a new function like:

  def turn_on_function1(self, entity, attribute, old, new, kwargs):
    self.turn_on_function3()
  def turn_on_function2(self, kwargs):
    self.turn_on_function3()
  def turn_on_function3(self):
    self.turn_on(self.args["switchID"])
    ... do all other stuff you like to happen in both situations

You are so awesome @ReneTode thank you so much for all of your help this week as I continue with my learning. I think I’m getting better at this and as you can tell from my first scripts to this I am starting to get a little more daring. I’m of course no where near some of the awesome people on here but I’m getting better :slight_smile:

1 Like