def __init__(self, prefs, logger):
        self.prefs = prefs
        if prefs.get('logging', 'enabled', default=False):
            self.log = logger
            self.log.setLevel(prefs.get('logging', 'level', default=20))
        self.soon = prefs.get('soon', default=600)
        self.interval = prefs.get('check_interval', default=60)
        self.credentials = self.get_credentials()
        self.validator = CalendarQuery()
        self.suffix = prefs.get('google_calendar', 'suffix', default=None)

        self.http = self.credentials.authorize(httplib2.Http())
        self.service = discovery.build('calendar', 'v3', http=self.http)
        self.hue = phue.Bridge(prefs.get('philips_hue', 'ip'),
                               prefs.get('philips_hue', 'token'))
        # The Phue library is finicky, won't load lights until invoked
        self.lights = self.hue.lights_by_name
        self.log.info("Lights found: %s"% ", ".join(
            l.name for l in self.hue.lights))

        self.calendars = self._build_calendars(
            self.prefs.get('google_calendar', default={}))
class Application(object):
    def __init__(self, prefs, logger):
        self.prefs = prefs
        if prefs.get('logging', 'enabled', default=False):
            self.log = logger
            self.log.setLevel(prefs.get('logging', 'level', default=20))
        self.soon = prefs.get('soon', default=600)
        self.interval = prefs.get('check_interval', default=60)
        self.credentials = self.get_credentials()
        self.validator = CalendarQuery()
        self.suffix = prefs.get('google_calendar', 'suffix', default=None)

        self.http = self.credentials.authorize(httplib2.Http())
        self.service = discovery.build('calendar', 'v3', http=self.http)
        self.hue = phue.Bridge(prefs.get('philips_hue', 'ip'),
                               prefs.get('philips_hue', 'token'))
        # The Phue library is finicky, won't load lights until invoked
        self.lights = self.hue.lights_by_name
        self.log.info("Lights found: %s"% ", ".join(
            l.name for l in self.hue.lights))

        self.calendars = self._build_calendars(
            self.prefs.get('google_calendar', default={}))

    def light_for_name(self, name):
        return self.lights.get(name, None)

    def _suffix_string(self, _dict, accessor="calendar"):
        _dict[accessor] = _dict.pop(accessor) + self.suffix
        return _dict

    def _merge_settings(self, key, localcontext):
        toplevel = self.prefs.get(key, default={})
        if type(toplevel) is dict and type(localcontext.get(key, {})) is dict:
            toplevel.update(localcontext.get(key, {}))
            return toplevel
        else:
            return localcontext.get(key, None) or toplevel or None

    def _build_one_cal(self, settings):
        cal = settings['calendar']
        colors = self._merge_settings('colors', settings)
        bri = self._merge_settings('bri', settings)
        light = self.light_for_name(
            settings['light'])
        if not light:
            self.log.error("%s is not found, skipping." % settings['light'])
            return None
        # Suffixes can be negated.
        if self.suffix and settings.get("suffix", True):
            cal += self.suffix
        _settings_string = "bri: %s, colors: %s" % (bri or "default",
                                                    colors or "default")
        hrr_name = settings.get('name', cal)
        self.log.info("%s with settings %s", hrr_name, _settings_string)
        return CalendarResource(cal, light, self.log, hrr_name, bri, colors)

    def _build_calendars(self, cal_settings):
        cals = cal_settings.get('light_maps', [])
        # if self.suffix:
        #     cals = map(self._suffix_string, cals)
        return [i for i in map(self._build_one_cal, cals) if i is not None]

    @staticmethod
    def get_credentials():
        """Gets valid user credentials from storage.

        If nothing has been stored, or if the stored credentials are invalid,
        the OAuth2 flow is completed to obtain the new credentials.

        Returns:
            Credentials, the obtained credential.
        """
        # Method is from google's quickstart.
        home_dir = os.path.expanduser('~')
        credential_dir = os.path.join(home_dir, '.credentials')
        if not os.path.exists(credential_dir):
            os.makedirs(credential_dir)
        credential_path = os.path.join(credential_dir,
                                    'calendar-quickstart.json')

        store = oauth2client.file.Storage(credential_path)
        credentials = store.get()
        SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
        CLIENT_SECRET_FILE = 'client_secret.json'
        if not credentials or credentials.invalid:
            flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
            flow.user_agent = "Gcal-Hue"
            # if flags:
            #     credentials = tools.run_flow(flow, store, flags)
            # else: # Needed only for compatability with Python 2.6
            #     credentials = tools.run(flow, store)
            credentials = tools.run(flow, store)
            print 'Storing credentials to ' + credential_path
        return credentials

    @staticmethod
    def format_into_iso(date_object):
        return date_object.strftime('%Y-%m-%dT%H:%M:%S.%f%z')

    @staticmethod
    def nows():
        return datetime.datetime.utcnow().isoformat() + 'Z'

    @staticmethod
    def now():
        return datetime.datetime.now(pytz.utc)

    def search_calendar(self, calendar, time=None,
                        result_count=None, strict=False):
        _results = result_count or 2
        if time and type(time) is not str:
            _time = self.format_into_iso(time)
        else:
            _time = time or self.nows()

        query, errors = self.validator.load(self.service.events().list(
            calendarId=calendar, timeMin=_time, maxResults=_results,
            singleEvents=True, orderBy="startTime"
        ).execute())

        if errors and strict:
            raise ValueError(errors)
        return query

    def error_and_exit(self, error):
        self.log.error(repr(error))
        sys.exit(1)

    def events(self, *args, **kwargs):
        return self.search_calendar(*args, **kwargs).get('items', [])

    def event_temporal_relation(self, event, time=None):
        """ Determine the relation of "now" to the top event.
        """
        time = time or self.now()
        event_start = event['start']['dateTime']
        if not event_start.tzinfo:
            event_start = pytz.utc.localize(event_start)
        event_end = event['end']['dateTime']
        if not event_end.tzinfo:
            event_end = pytz.utc.localize(event_end)
        if event_start < time < event_end:
            return "now", event
        if event_start > time and (event_start - time).seconds <= self.soon:
            return "soon", event
        return "clear", event

    def map_relations(self, events, time=None):
        return map(lambda x: self.event_temporal_relation(x, time), events)

    def run_task(self):
        if not self.calendars:
            self.error_and_exit("No Calendars")
        for cal in self.calendars:
            if cal.errors:
                continue
            try:
                events = self.events(cal.calendar)
            except Exception, e:
                if e.resp.get("status") == "404":
                    self.log.error("%s does not exist." % cal.calendar)
                else:
                    self.log.error(repr(e))
                cal.errors = True
                continue
            if events:
                relations = map(self.event_temporal_relation, events)
                cal.change_for_status(relations)
        if all(i.errors for i in self.calendars):
            self.error_and_exit("All calendars are failing")