code: def waituntil(conditionfunc, timeout=None):
      
      app = NSApplication.sharedApplication()
      starttime = time.time()
      if timeout is None:
          timeout = NSDate.distantFuture().timeIntervalSinceNow()
      if not isinstance(timeout, (int, float)):
          raise TypeError("timeout must be int or float, was %s" % \
                  type(timeout))
      endtime = starttime + timeout
      while True:
          currtime = time.time()
          if currtime >= endtime:
              return False
          # use WAIT_MAX_TIMEOUT, don't wait forever in case of KeyboardInterrupt
          e = app.nextEventMatchingMask_untilDate_inMode_dequeue_(NSAnyEventMask, NSDate.dateWithTimeIntervalSinceNow_(min(endtime - currtime, WAIT_MAX_TIMEOUT)), NSDefaultRunLoopMode, True)
          if e is not None:
              if (e.type() == NSApplicationDefined and e.subtype() == LIGHTBLUE_NOTIFY_ID):
                  if conditionfunc():
                      return True
              else:
                  app.postEvent_atStart_(e, True)
docs: Waits until conditionfunc() returns true, or <timeout> seconds have passed.
      (If timeout=None, this waits indefinitely until conditionfunc() returns
      true.) Returns false if the process timed out, otherwise returns true.
  
      Note!! You must call interruptwait() when you know that conditionfunc()
      should be checked (e.g. if you are waiting for data and you know some data
      has arrived) so that this can check conditionfunc(); otherwise it will just
      continue to wait. (This allows the function to wait for an event that is
      sent by interruptwait() instead of polling conditionfunc().)
  
      This allows the caller to wait while the main event loop processes its
      events. This must be done for certain situations, e.g. to receive socket
      data or to accept client connections on a server socket, since IOBluetooth
      requires the presence of an event loop to run these operations.
  
      This function doesn't need to be called if there is something else that is
      already processing the main event loop, e.g. if called from within a Cocoa
      application.