Datetime.time -15 minutes

I’m fairly new to python but seriously there’s no way to do this?

I’m trying to setup a daily alarm so I want to use datetime.time not datetime.datetime so as not to have to deal with re-initializing my date every day, I just want it to run at the given time every day.

I’m trying to follow the example as in the battery.py example app, but I also need to do several things at various times before my “alarm” datetime.time.

There’s seriously no way to do a delta like this with the datetime.time object so I can do something 15 minutes before datetime.time, do something else 10 minutes before datetime.time, etc?

Edit: Sorry didn’t read OP properly… this is not the answer you are looking for

This should do the trick:

datetime.datetime.now() - datetime.timedelta(minutes=15)

Lol. Thanks for trying. I guess I’m going to have to use datetime.datetime and re-initialize it every day after midnight…

Check this thread out:

In [1]: import datetime
In [2]: my_time = datetime.time(21, 30)
In [3]: (datetime.datetime.combine(datetime.date(1, 1, 1), my_time) - datetime.timedelta(minutes=15)).time()
Out[3]: datetime.time(21, 15)
1 Like

You could also use datetime.datetime and just always replace the date to 1/1/1 on every occurrence or something like that,

oh sh*t that works! Thanks dude!

the easiest way to do stuff like that is to start at the beginning.

if you want to run something 15 mins before a certain time and after that some more things you could think of it this way:

    minutes_before = 15
    action2_minutes_before = 10

    self.alarmtime = minutes_before * 60
    self.action2 = (minutes_before - action2_minutes_before)*60
    hours = round(minutes_before/60)
    minutes = minutes_before - (hours*60)
    time = datetime.time(6- hours, -(0-minutes), 0) 
    self.run_daily(self.prealarm, time)
    
  def prealarm(self, kwargs):
    #do the stuff you want to happen  before the alarm
    self.run_in(self.something_else,self.action2)
    self.run_in(self.alarm, self.alarmtime)

  def something_else(self, kwargs):
    # do stuff that you want 10 mins before alarm

  def alarm(self, kwargs):
    # do the actual alarm

now you can set how many minutes before you want stuff to happen and another action before as wel.

That’s interesting and maybe necessary. I was trying to do something like this, but it didn’t work last night. I’m wondering if my several run_daily callbacks blocked each other:

import appdaemon.plugins.hass.hassapi as hass
import datetime
import time

# Declare Class
class WakeUp(hass.Hass):
  #initialize() function which will be called at startup and reload
  def initialize(self):
    self.wakeup = datetime.time(8, 15, 0)
    self.espresso_on = datetime.time(7, 30, 0)
    self.furnace_on = datetime.time(8, 08, 0)
    self.lights_on = datetime.time(8, 00, 0)

    self.listen_state(self.alarm_set_event, self.args["alarm_switch"], attribute="brightness")
    self.run_daily(self.run_espresso, self.espresso_on)
    self.run_daily(self.run_furnace, self.furnace_on)
    self.run_daily(self.run_fade, self.lights_on)
    self.run_daily(self.run_wakeup, self.wakeup)
    #self.log("listening: {}".format(self.args["alarm_switch"]))

  def alarm_set_event(self, entity, attrubite, old, new, kwargs):
    self.log("New: {}".format(new))
    #self.log("Old: {}".format(old))
    if new == 127:
      self.wakeup = datetime.time(5, 00, 0)
    elif new == 130:
      self.wakeup = datetime.time(5, 15, 0)
    elif new == 132:
      self.wakeup = datetime.time(5, 30, 0)
    elif new == 135:
      self.wakeup = datetime.time(5, 45, 0)
    elif new == 153:
      self.wakeup = datetime.time(6, 00, 0)
    elif new == 155:
      self.wakeup = datetime.time(6, 15, 0)
    elif new == 158:
      self.wakeup = datetime.time(6, 30, 0)
    elif new == 160:
      self.wakeup = datetime.time(6, 45, 0)
    elif new == 178:
      self.wakeup = datetime.time(7, 00, 0)
    elif new == 181:
      self.wakeup = datetime.time(7, 15, 0)
    elif new == 183:
      self.wakeup = datetime.time(7, 30, 0)
    elif new == 186:
      self.wakeup = datetime.time(7, 45, 0)
    elif new == 204:
      self.wakeup = datetime.time(8, 00, 0)
    elif new == 206:
      self.wakeup = datetime.time(8, 15, 0)
    elif new == 209:
      self.wakeup = datetime.time(8, 30, 0)
    elif new == 211:
      self.wakeup = datetime.time(8, 45, 0)
    elif new == 229:
      self.wakeup = datetime.time(9, 00, 0)
    elif new == 232:
      self.wakeup = datetime.time(9, 15, 0)
    elif new == 234:
      self.wakeup = datetime.time(9, 30, 0)
    elif new == 237:
      self.wakeup = datetime.time(9, 45, 0)
    elif new == 25:
      self.wakeup = datetime.time(10, 00, 0)
    elif new == 28:
      self.wakeup = datetime.time(10, 15, 0)
    elif new == 30:
      self.wakeup = datetime.time(10, 30, 0)
    elif new == 33:
      self.wakeup = datetime.time(10, 45, 0)

    self.espresso_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=45)).time()
    self.lights_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=15)).time()
    self.furnace_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=7)).time()
    self.log("Alarm Set for: {}".format(self.wakeup))
    self.log("Espresso Machine On: {}".format(self.espresso_on))
    self.log("Light Fade Start: {}".format(self.lights_on))
    self.log("Furnace On: {}".format(self.furnace_on))

  def run_fade(self, kwargs):
      self.fire_event("MODE_CHANGE", mode = "Morning")
      for b in range(1,15):
        self.turn_on("group.bedroom_lights", brightness_pct=b)
        time.sleep(58)

  def run_espresso(self, kwargs):
      self.turn_on("switch.espresso_machine")

  def run_furnace(self, kwargs):
      self.call_service("climate/set_operation_mode", entity_id = "climate.Heating", operation_mode = "Heating")
      self.call_service("climate/set_operation_mode", entity_id = "climate.Heating", operation_mode = "Heating")
      self.turn_on("switch.hotwater_tank")

  def run_wakeup(self, kwargs):
      self.fire_event("MODE_CHANGE", mode = "Present")
      self.turn_on("group.bedroom_lights", brightness_pct=60)
      self.turn_on("switch.hotwater_loop")

Changing these will not change the time previously scheduled by the run_daily call. You will have to cancel the timer and call it with the fresh parameters.

But if you are going to schedule it differently every day, I suggest using run_at rather than having to cancel an existing timer.

1 Like

Well now I’m a bit confused.

If I use run_at will I have to re-initialize it every day? I can just do multiple run_at callbacks in a way that I can’t do multiple run_daily callbacks?

I don’t quite understand how the blocking works here. My self.run_fade function is going to take about 15m to complete. Will it block the subsequent self.run_at callbacks that will fire in the time it’s running? Sorry my terminology isn’t quite right…

    self.run_at(self.run_espresso, self.espresso_on)
    self.run_at(self.run_furnace, self.furnace_on)
    self.run_at(self.run_fade, self.lights_on)
    self.run_at(self.run_wakeup, self.wakeup)

Yeah run_at is not happy. I think it takes a datetime.datetime not datetime.time:

File “/usr/lib/python3.6/site-packages/appdaemon/appdaemon.py”, line 1581, in init_object
init()
File “/config/appdaemon/apps/wakeup.py”, line 15, in initialize
self.run_at(self.run_espresso, self.espresso_on)
File “/usr/lib/python3.6/site-packages/appdaemon/appapi.py”, line 359, in run_at
if start < now:
TypeError: ‘<’ not supported between instances of ‘datetime.time’ and ‘datetime.datetime’

what GP is saying is that the initialise function is only called once.
the time from the run daily cant be changed afterwards.
you could like he said use a run_at in the alarm_set callback if you would like to change the times based on an alarm.
and indeed you need a datettime in that case, so you need to change it from the date on.

I guess I’m not understanding why I can’t have multiple run_daily objects in parallel?

Oh wait… I suppose I’m starting to get it. so yeah I need to move the run_daily callbacks into the alarm_set_event function and then add handles for each so I can cancel them before re-initializing on an alarm_set_event?

I guess the only thing I’m still unclear about is if the best practice is to run multiple run_daily events or to run a single_run_daily with “run_in” steps inside of it? Do multiple timer callbacks block each other?

UPDATED:

import appdaemon.plugins.hass.hassapi as hass
import datetime
import time

# Declare Class
class WakeUp(hass.Hass):
  #initialize() function which will be called at startup and reload
  def initialize(self):
    self.handle_espresso = None
    self.handle_hvac = None
    self.handle_lights = None
    self.handle_wakeup = None
    # Default to 8:15
    self.wakeup = datetime.time(8, 15, 0)
    self.set_timers()

    self.listen_state(self.alarm_set_event, self.args["alarm_switch"], attribute="brightness")
    #self.log("listening: {}".format(self.args["alarm_switch"]))

  def alarm_set_event(self, entity, attrubite, old, new, kwargs):
    if new == 127:
      self.wakeup = datetime.time(5, 00, 0)
    elif new == 130:
      self.wakeup = datetime.time(5, 15, 0)
    elif new == 132:
      self.wakeup = datetime.time(5, 30, 0)
    elif new == 135:
      self.wakeup = datetime.time(5, 45, 0)
    elif new == 153:
      self.wakeup = datetime.time(6, 00, 0)
    elif new == 155:
      self.wakeup = datetime.time(6, 15, 0)
    elif new == 158:
      self.wakeup = datetime.time(6, 30, 0)
    elif new == 160:
      self.wakeup = datetime.time(6, 45, 0)
    elif new == 178:
      self.wakeup = datetime.time(7, 00, 0)
    elif new == 181:
      self.wakeup = datetime.time(7, 15, 0)
    elif new == 183:
      self.wakeup = datetime.time(7, 30, 0)
    elif new == 186:
      self.wakeup = datetime.time(7, 45, 0)
    elif new == 204:
      self.wakeup = datetime.time(8, 00, 0)
    elif new == 206:
      self.wakeup = datetime.time(8, 15, 0)
    elif new == 209:
      self.wakeup = datetime.time(8, 30, 0)
    elif new == 211:
      self.wakeup = datetime.time(8, 45, 0)
    elif new == 229:
      self.wakeup = datetime.time(9, 00, 0)
    elif new == 232:
      self.wakeup = datetime.time(9, 15, 0)
    elif new == 234:
      self.wakeup = datetime.time(9, 30, 0)
    elif new == 237:
      self.wakeup = datetime.time(9, 45, 0)
    elif new == 25:
      self.wakeup = datetime.time(10, 00, 0)
    elif new == 28:
      self.wakeup = datetime.time(10, 15, 0)
    elif new == 30:
      self.wakeup = datetime.time(10, 30, 0)
    elif new == 33:
      self.wakeup = datetime.time(10, 45, 0)
    else:
      self.wakeup = datetime.time(8, 15, 0)
    self.set_timers()

  def set_timers(self):
    self.cancel()
    self.espresso_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=45)).time()
    self.lights_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=15)).time()
    self.hvac_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=7)).time()
    self.handle_espresso = self.run_daily(self.run_espresso, self.espresso_on)
    self.handle_hvac = self.run_daily(self.run_hvac, self.hvac_on)
    self.handle_lights = self.run_daily(self.run_fade, self.lights_on)
    self.handle_wakeup = self.run_daily(self.run_wakeup, self.wakeup)
    self.log("Alarm Set for: {}".format(self.wakeup))
    self.log("Espresso Machine On: {}".format(self.espresso_on))
    self.log("Light Fade Start: {}".format(self.lights_on))
    self.log("HVAC On: {}".format(self.hvac_on))

  def run_fade(self, kwargs):
      self.log("Starting lights")
      self.fire_event("MODE_CHANGE", mode = "Morning")
      for b in range(1,15):
        self.turn_on("group.bedroom_lights", brightness_pct=b)
        time.sleep(58)

  def run_espresso(self, kwargs):
      self.log("Starting Espresso Machine")
      self.turn_on("switch.espresso_machine")

  def run_hvac(self, kwargs):
      self.log("Starting HVAC")
      # Why twice? Dunno but it works.
      self.call_service("climate/set_operation_mode", entity_id = "climate.Heating", operation_mode = "Heating")
      self.call_service("climate/set_operation_mode", entity_id = "climate.Heating", operation_mode = "Heating")
      self.turn_on("switch.hotwater_tank")

  def run_wakeup(self, kwargs):
      self.log("Starting Wakeup")
      self.fire_event("MODE_CHANGE", mode = "Present")
      self.turn_on("group.bedroom_lights", brightness_pct=60)
      self.turn_on("switch.hotwater_loop")

  def cancel(self):               
    self.log("Cancel existing timers")
    self.cancel_timer(self.handle_espresso)
    self.cancel_timer(self.handle_lights)
    self.cancel_timer(self.handle_hvac)
    self.cancel_timer(self.handle_wakeup)

you can run as many run_dailies as you want to. and they wont block each other.
but if you are talking about best practice then you need to change some things.

  1. dont use a light to set youre alarm. do you have a list beside your PC to know how to set your brightness to set your alarm to a certain time? if you want a slider then use a input_slider, but even better is an input_datetime.
  2. dont use time.sleep that is very bad practice and there is even a warning in the docs about it. it will put your app on hold preventing it from any other possible things to do. in AD 3.0.2 thats even worse then in the new version which is still to come. it will hold up a thread. do that with a few apps and AD will hold completely
    in your case it makes it also impossible to run wake_up before run_fade has ended.

Thanks for the tips. I now recall seeing something about using sleep in the docs. I suppose I can do something with run_in instead. I’d originally hoped to use the “transition” feature of the light.device but I don’t think the device itself is going to support a transition of 900 seconds. The goal is to fade the light up from 1-15% over 15 minutes or so. This is a recreation of behaviour I had setup on SmartThings with webCoRE.

As for using a light to set the alarm, it’s not actually a light, it’s a virtual dimmer on a hubitat device addressed via MQTT. This dimmer is set to a given level using Alexa routines, so that I can say something like, “Alexa, set wakeup to eight thirty”, which sets the dimmer to 82%(or 209), which is then interpreted by the app into a time.

I was doing this scheme in webCoRE and while it takes a bit of initial setup and the resolution isn’t that great, it works well enough to set an “alarm” via alexa that is able to run pre-tasks and doesn’t trigger an actual alexa alarm.

At some point in the future I may look into doing this via Alexa and HA directly, but this works for now and have other fish to fry. I need to get back to the full functionality I had under SmartThings… when SmartThings felt like working that is. :wink:

wauw, thats a lot of work to be able to set a timer by speech :wink:

but indeed then better use a run_in

def start_lights(self, kwargs):
      self.log("Starting lights")
      self.fire_event("MODE_CHANGE", mode = "Morning")
      self.brightness = 1
      self.run_fade(self)
def run_fade(self, kwargs):
      if self.brightness < 15:
          self.turn_on("group.bedroom_lights", brightness_pct=self.brightness)
          self.brightness = selfbrightness +1
          self.run_in(self.run_fade,58)

Dude, thanks for the code! I’m still getting used to python, so I have to think hard about even the littlest things.

As for the alexa alarm thing, the lengthiest part of it was setting up the alexa routines, which you have to do on your phone only for some reason. The web gui doesn’t let you setup a routine.

I only did them for every 15 minutes, and only for the hours I’m likely to want to wake up, so 5-11am basically, 24 in total. It wasn’t that bad, and in my case they’re already all setup anyway.

1 Like

Hmm… so it looks like one of my run_daily’s is not going off.

The first, run_espresso one fires at T-45 minutes and completes almost immediately
The second one, run_lights fires at T-15 minutes and takes 15m to complete
The third one, run_hvac fires at T-7 minutes and this is the one that’s not working
The final one, run_wakeup fires at T-0 minutes, which is after the third one has completed has completed.

It sure looks like the third run_daily won’t go off while the second one is still running.

It’s easy enough to work around by just handling those tasks inside the other function, but it sure looks like you can’t have 2 ongoing overlapping run_daily threads. It looks that way… doesn’t mean it is though.

if you still got the time.sleep in it then that is true.
the time.sleep keeps the thread busy and when its released the time from the third has passed.

No time.sleep(). I think I may redo this using only one run_daily with run_in’s inside of that.

import appdaemon.plugins.hass.hassapi as hass
import datetime

# Declare Class
class WakeUp(hass.Hass):
  #initialize() function which will be called at startup and reload
  def initialize(self):
    self.handle_espresso = None
    self.handle_hvac = None
    self.handle_lights = None
    self.handle_wakeup = None
    # Default to 8:15
    self.wakeup = datetime.time(8, 15, 0)
    self.set_timers()

    self.listen_state(self.alarm_set_event, self.args["alarm_switch"], attribute="brightness")
    #self.log("listening: {}".format(self.args["alarm_switch"]))

  def alarm_set_event(self, entity, attrubite, old, new, kwargs):
    if new == 127:
      self.wakeup = datetime.time(5, 00, 0)
    elif new == 130:
      self.wakeup = datetime.time(5, 15, 0)
    elif new == 132:
      self.wakeup = datetime.time(5, 30, 0)
    elif new == 135:
      self.wakeup = datetime.time(5, 45, 0)
    elif new == 153:
      self.wakeup = datetime.time(6, 00, 0)
    elif new == 155:
      self.wakeup = datetime.time(6, 15, 0)
    elif new == 158:
      self.wakeup = datetime.time(6, 30, 0)
    elif new == 160:
      self.wakeup = datetime.time(6, 45, 0)
    elif new == 178:
      self.wakeup = datetime.time(7, 00, 0)
    elif new == 181:
      self.wakeup = datetime.time(7, 15, 0)
    elif new == 183:
      self.wakeup = datetime.time(7, 30, 0)
    elif new == 186:
      self.wakeup = datetime.time(7, 45, 0)
    elif new == 204:
      self.wakeup = datetime.time(8, 00, 0)
    elif new == 206:
      self.wakeup = datetime.time(8, 15, 0)
    elif new == 209:
      self.wakeup = datetime.time(8, 30, 0)
    elif new == 211:
      self.wakeup = datetime.time(8, 45, 0)
    elif new == 229:
      self.wakeup = datetime.time(9, 00, 0)
    elif new == 232:
      self.wakeup = datetime.time(9, 15, 0)
    elif new == 234:
      self.wakeup = datetime.time(9, 30, 0)
    elif new == 237:
      self.wakeup = datetime.time(9, 45, 0)
    elif new == 25:
      self.wakeup = datetime.time(10, 00, 0)
    elif new == 28:
      self.wakeup = datetime.time(10, 15, 0)
    elif new == 30:
      self.wakeup = datetime.time(10, 30, 0)
    elif new == 33:
      self.wakeup = datetime.time(10, 45, 0)
    else:
      self.wakeup = datetime.time(8, 15, 0)
    self.set_timers()

  def set_timers(self):
    self.cancel()
    self.espresso_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=45)).time()
    self.lights_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=15)).time()
    self.hvac_on = (datetime.datetime.combine(datetime.date(1, 1, 1), self.wakeup) - datetime.timedelta(minutes=7)).time()
    self.handle_espresso = self.run_daily(self.run_espresso, self.espresso_on)
    self.handle_hvac = self.run_daily(self.run_hvac, self.hvac_on)
    self.handle_lights = self.run_daily(self.run_lights, self.lights_on)
    self.handle_wakeup = self.run_daily(self.run_wakeup, self.wakeup)
    self.log("Alarm Set for: {}".format(self.wakeup))
    self.log("Espresso Machine On: {}".format(self.espresso_on))
    self.log("Light Fade Start: {}".format(self.lights_on))
    self.log("HVAC On: {}".format(self.hvac_on))

  def run_lights(self, kwargs):
      self.log("Starting lights")
      self.fire_event("MODE_CHANGE", mode = "Morning")
      self.brightness = 1
      self.fade(self)

  def run_espresso(self, kwargs):
      self.log("Starting Espresso Machine")
      self.turn_on("switch.espresso_machine")

  def run_hvac(self, kwargs):
    self.log("Starting HVAC")
    for i in range(0,3):
      self.call_service("climate/set_operation_mode", entity_id = "climate.Heating", operation_mode = "Heating")
    self.turn_on("switch.hotwater_tank")

  def run_wakeup(self, kwargs):
      self.log("Starting Wakeup")
      self.fire_event("MODE_CHANGE", mode = "Present")
      self.turn_on("group.bedroom_lights", brightness_pct=60)
      self.turn_on("switch.hotwater_loop")

  def fade(self, kwargs):
    if self.brightness < 15:
      self.turn_on("group.bedroom_lights", brightness_pct=self.brightness)
      self.brightness = self.brightness +1
      self.run_in(self.fade,58)

  def cancel(self):               
    self.log("Cancel existing timers")
    self.cancel_timer(self.handle_espresso)
    self.cancel_timer(self.handle_lights)
    self.cancel_timer(self.handle_hvac)
    self.cancel_timer(self.handle_wakeup)