Esempio n. 1
0
 def run_daily(self, callback, start, **kwargs):
     name = self.name
     now = ha.get_now()
     today = now.date()
     event = datetime.datetime.combine(today, start)
     if event < now:
         event = event + datetime.timedelta(days=1)
     handle = self.run_every(callback, event, 24 * 60 * 60, **kwargs)
     return handle
Esempio n. 2
0
 def run_at(self, callback, start, **kwargs):
     name = self.name
     now = ha.get_now()
     if start < now:
         raise ValueError("{}: run_at() Start time must be "
                          "in the future".format(self.name))
     exec_time = start.timestamp()
     handle = ha.insert_schedule(name, exec_time, callback, False, None,
                                 **kwargs)
     return handle
Esempio n. 3
0
 def run_minutely(self, callback, start, **kwargs):
     name = self.name
     now = ha.get_now()
     if start is None:
         event = now + datetime.timedelta(minutes=1)
     else:
         event = now
         event = event.replace(second=start.second)
         if event < now:
             event = event + datetime.timedelta(minutes=1)
     handle = self.run_every(callback, event, 60, **kwargs)
     return handle
Esempio n. 4
0
 def run_once(self, callback, start, **kwargs):
     name = self.name
     now = ha.get_now()
     today = now.date()
     event = datetime.datetime.combine(today, start)
     if event < now:
         one_day = datetime.timedelta(days=1)
         event = event + one_day
     exec_time = event.timestamp()
     handle = ha.insert_schedule(name, exec_time, callback, False, None,
                                 **kwargs)
     return handle
Esempio n. 5
0
 def run_every(self, callback, start, interval, **kwargs):
     name = self.name
     now = ha.get_now()
     if start < now:
         raise ValueError("start cannot be in the past")
     ha.log(
         conf.logger, "DEBUG",
         "Registering run_every starting {} in {}s intervals for {}".format(
              start, interval, name
         )
     )
     exec_time = start.timestamp()
     handle = ha.insert_schedule(name, exec_time, callback, True, None,
                                 interval=interval, **kwargs)
     return handle
Esempio n. 6
0
def update_sun():

    #now = datetime.datetime.now(conf.tz)
    now = conf.tz.localize(ha.get_now())
    mod = -1
    while True:
        try:
            next_rising_dt = conf.location.sunrise(
                now + datetime.timedelta(days=mod), local=False)
            if next_rising_dt > now:
                break
        except astral.AstralError:
            pass
        mod += 1

    mod = -1
    while True:
        try:
            next_setting_dt = (conf.location.sunset(
                now + datetime.timedelta(days=mod), local=False))
            if next_setting_dt > now:
                break
        except astral.AstralError:
            pass
        mod += 1

    old_next_rising_dt = conf.sun.get("next_rising")
    old_next_setting_dt = conf.sun.get("next_setting")
    conf.sun["next_rising"] = next_rising_dt
    conf.sun["next_setting"] = next_setting_dt

    if old_next_rising_dt != None and old_next_rising_dt != conf.sun[
            "next_rising"]:
        #dump_schedule()
        process_sun("next_rising")
        #dump_schedule()
    if old_next_setting_dt != None and old_next_setting_dt != conf.sun[
            "next_setting"]:
        #dump_schedule()
        process_sun("next_setting")
Esempio n. 7
0
def run():

  global was_dst
  global last_state
  global reading_messages

  # Take a note of DST

  was_dst = is_dst()

  # Setup sun

  update_sun()

  # Create Worker Threads
  for i in range(conf.threads):
     t = threading.Thread(target=worker)
     t.daemon = True
     t.start()

  # Read apps and get HA State before we start the timer thread
  get_ha_state()
  ha.log(conf.logger, "INFO", "Got initial state")
  # Load apps
  readApps(True)
  last_state = ha.get_now()

  # Create timer thread

  t = threading.Thread(target=timer_thread)
  t.daemon = True
  t.start()

  # Enter main loop

  first_time = True

  while True:
    try:
      if first_time == False:
        # Get initial state
        get_ha_state()
        ha.log(conf.logger, "INFO", "Got initial state")
        # Load apps
        readApps(True)
        last_state = ha.get_now()

      #
      # Fire HA_STARTED and APPD_STARTED Events
      #
      if first_time == True:
        process_event({"event_type": "appd_started", "data": {}})
        first_time = False
      else:
        process_event({"event_type": "ha_started", "data": {}})

      headers = {'x-ha-access': conf.ha_key}
      reading_messages = True
      messages = SSEClient("{}/api/stream".format(conf.ha_url), verify = False, headers = headers, retry = 3000)
      for msg in messages:
        process_message(msg)
    except:
      reading_messages = False
      conf.logger.warning("Not connected to Home Assistant, retrying in 5 seconds")
      if last_state == None:
        ha.log(conf.logger, "WARNING", '-'*60)
        ha.log(conf.logger, "WARNING", "Unexpected error:")
        ha.log(conf.logger, "WARNING", '-'*60)
        ha.log(conf.logger, "WARNING", traceback.format_exc())
        ha.log(conf.logger, "WARNING", '-'*60)
    time.sleep(5)
Esempio n. 8
0
def do_every_second(utc):

  global was_dst
  global last_state
    
  # Lets check if we are connected, if not give up.
  if not reading_messages:
    return
  try:

    #now = datetime.datetime.now()
    #now = now.replace(microsecond=0)
    now = datetime.datetime.fromtimestamp(utc)
    conf.now = utc

    # If we have reached endtime bail out
    
    if conf.endtime != None and ha.get_now() >= conf.endtime:
      ha.log(conf.logger, "INFO", "End time reached, exiting")
      os._exit(0)
      
    if conf.realtime:
      real_now = datetime.datetime.now().timestamp()
      delta = abs(utc - real_now)
      if delta > 1:
        ha.log(conf.logger, "WARNING", "Scheduler clock skew detected - delta = {} - resetting".format(delta))
        return real_now
        
    # Update sunrise/sunset etc.

    update_sun()

    # Check if we have entered or exited DST - if so, reload apps to ensure all time callbacks are recalculated

    now_dst = is_dst()
    if now_dst != was_dst:
      ha.log(conf.logger, "INFO", "Detected change in DST from {} to {} - reloading all modules".format(was_dst, now_dst))
      dump_schedule()
      ha.log(conf.logger, "INFO", "-" * 40)
      readApps(True)
      dump_schedule()
    was_dst = now_dst

    #dump_schedule()

    # Check to see if any apps have changed but only if we have valid state

    if last_state != None:
      readApps()

    # Check to see if config has changed

    check_config()

    # Call me suspicious, but lets update state form HA periodically in case we miss events for whatever reason
    # Every 10 minutes seems like a good place to start

    if  last_state != None and now - last_state > datetime.timedelta(minutes = 10):
      try:
        get_ha_state()
        last_state = now
      except:
        conf.log.warn("Unexpected error refreshing HA state - retrying in 10 minutes")

    # Check on Queue size

    if q.qsize() > 0 and q.qsize() % 10 == 0:
      conf.logger.warning("Queue size is {}, suspect thread starvation".format(q.qsize()))

    # Process callbacks

    #ha.log(conf.logger, "DEBUG", "Scheduler invoked at {}".format(now))
    for name in conf.schedule.keys():
      for entry in sorted(conf.schedule[name].keys(), key=lambda uuid: conf.schedule[name][uuid]["timestamp"]):
        #ha.log(conf.logger, "DEBUG", "{} : {}".format(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(conf.schedule[name][entry]["timestamp"])), time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(now))))
        if conf.schedule[name][entry]["timestamp"] <= utc:
          exec_schedule(name, entry, conf.schedule[name][entry])
        else:
          break
    for k, v in list(conf.schedule.items()):
      if v == {}:
        del conf.schedule[k]
        
    return utc

  except:
    ha.log(conf.error, "WARNING", '-'*60)
    ha.log(conf.error, "WARNING", "Unexpected error during do_every_second()")
    ha.log(conf.error, "WARNING", '-'*60)
    ha.log(conf.error, "WARNING", traceback.format_exc())
    ha.log(conf.error, "WARNING", '-'*60)
    if conf.errorfile != "STDERR" and conf.logfile != "STDOUT":
      # When explicitly logging to stdout and stderr, suppress
      # log messages abour writing an error (since they show up anyway)
      ha.log(conf.logger, "WARNING", "Logged an error to {}".format(conf.errorfile))
Esempio n. 9
0
def run():

    global was_dst
    global last_state
    global reading_messages

    ha.log(conf.logger, "DEBUG", "Entering run()")

    # Take a note of DST

    was_dst = is_dst()

    # Setup sun

    update_sun()

    ha.log(conf.logger, "DEBUG", "Creating worker threads ...")

    # Create Worker Threads
    for i in range(conf.threads):
        t = threading.Thread(target=worker)
        t.daemon = True
        t.start()

    ha.log(conf.logger, "DEBUG", "Done")

    # Read apps and get HA State before we start the timer thread
    ha.log(conf.logger, "DEBUG", "Calling HA for initial state")

    while last_state == None:
        try:
            get_ha_state()
            last_state = ha.get_now()
        except:
            ha.log(conf.logger, "WARNING", '-' * 60)
            ha.log(conf.logger, "WARNING", "Unexpected error:")
            ha.log(conf.logger, "WARNING", '-' * 60)
            ha.log(conf.logger, "WARNING", traceback.format_exc())
            ha.log(conf.logger, "WARNING", '-' * 60)
            ha.log(conf.logger, "WARNING",
                   "Not connected to Home Assistant, retrying in 5 seconds")
        time.sleep(5)

    ha.log(conf.logger, "INFO", "Got initial state")
    # Load apps

    ha.log(conf.logger, "DEBUG", "Reading Apps")

    readApps(True)

    # wait until all threads have finished initializing

    while True:
        with conf.threads_busy_lock:
            if conf.threads_busy == 0:
                break
            ha.log(
                conf.logger, "INFO",
                "Waiting for App initialization: {} remaining".format(
                    conf.threads_busy))
        time.sleep(1)

    ha.log(conf.logger, "INFO", "App initialization complete")

    # Create timer thread

    # First, update "now" for less chance of clock skew error
    if conf.realtime:
        conf.now = datetime.datetime.now().timestamp()

    ha.log(conf.logger, "DEBUG", "Starting timer thread")

    t = threading.Thread(target=timer_thread)
    t.daemon = True
    t.start()

    # Enter main loop

    first_time = True
    reading_messages = True

    while True:
        try:
            if first_time == False:
                # Get initial state
                get_ha_state()
                last_state = ha.get_now()
                ha.log(conf.logger, "INFO", "Got initial state")

                # Let the timer thread know we are in business, and give it time to tick at least once
                reading_messages = True
                time.sleep(2)

                # Load apps
                readApps(True)

                while True:
                    with conf.threads_busy_lock:
                        if conf.threads_busy == 0:
                            break
                        ha.log(
                            conf.logger, "INFO",
                            "Waiting for App initialization: {} remaining".
                            format(conf.threads_busy))
                    time.sleep(1)

                ha.log(conf.logger, "INFO", "App initialization complete")

            #
            # Fire HA_STARTED and APPD_STARTED Events
            #
            if first_time == True:
                process_event({"event_type": "appd_started", "data": {}})
                first_time = False
            else:
                process_event({"event_type": "ha_started", "data": {}})

            headers = {'x-ha-access': conf.ha_key}
            messages = SSEClient("{}/api/stream".format(conf.ha_url),
                                 verify=False,
                                 headers=headers,
                                 retry=3000)
            for msg in messages:
                process_message(msg)
        except:
            reading_messages = False
            ha.log(conf.logger, "WARNING",
                   "Not connected to Home Assistant, retrying in 5 seconds")
            if last_state == None:
                ha.log(conf.logger, "WARNING", '-' * 60)
                ha.log(conf.logger, "WARNING", "Unexpected error:")
                ha.log(conf.logger, "WARNING", '-' * 60)
                ha.log(conf.logger, "WARNING", traceback.format_exc())
                ha.log(conf.logger, "WARNING", '-' * 60)
        time.sleep(5)
Esempio n. 10
0
def today_is_constrained(days):
    day = ha.get_now().weekday()
    daylist = [ha.day_of_week(day) for day in days.split(",")]
    if day in daylist:
        return False
    return True