def _create_recurring_alarm(self, when, recur): # 'recur' is a set of day index strings, e.g. {"3", "4"} # convert rule into an iCal rrule # TODO: Support more complex alarms, e.g. first monday, monthly, etc rule = "" abbr = ["SU", "MO", "TU", "WE", "TH", "FR", "SA"] days = [] for day in recur: days.append(abbr[int(day)]) if days: rule = "FREQ=WEEKLY;INTERVAL=1;BYDAY=" + ",".join(days) if when and rule: when = to_utc(when) # Create a repeating rule that starts in the past, enough days # back that it encompasses any repeat. past = when + timedelta(days=-45) rr = rrulestr("RRULE:" + rule, dtstart=past) now = to_utc(now_utc()) # Get the first repeat that happens after right now next = rr.after(now) return { "timestamp": to_utc(next).timestamp(), "repeat_rule": rule, } else: return { "timestamp": None, "repeat_rule": rule, }
def get_local_datetime(self, location, dtUTC=None): print("get_local_datetime") #print("6") if not dtUTC: #print("6.1") print("dtUTC not set") dtUTC = now_utc() if self.display_tz: #print("6.2") print("display timezone available") # montre les dates demandées par l'utilisateur dans certaines timeezone tz = self.display_tz else: #print("6.3") print("display timezone not available") tz = self.get_timezone(self.location_timezone) if location: print("location available") tz = self.get_timezone(location) if not tz: self.speak_dialog("timezone.pas.trouve", {"location": location}) return None #print("6.4") print("return from get_local_datetime") return dtUTC.astimezone(tz)
def _curate_alarms(self, curation_limit=1): """[summary] curation_limit (int, optional): Seconds past expired at which to remove the alarm """ alarms = [] now_ts = to_utc(now_utc()).timestamp() for alarm in self.settings["alarm"]: # Alarm format == [timestamp, repeat_rule[, orig_alarm_timestamp]] if alarm["timestamp"] < now_ts: if alarm["timestamp"] < (now_ts - curation_limit): # skip playing an old alarm if alarm["repeat_rule"]: # reschedule in future if repeat rule exists alarms.append(self._next_repeat(alarm)) else: # schedule for right now, with the # third entry as the original base time base = alarm["name"] if alarm["name"] == ''\ else alarm["timestamp"] alarms.append({ "timestamp": now_ts+1, "repeat_rule": alarm["repeat_rule"], "name": alarm["name"], "snooze": base }) else: alarms.append(alarm) alarms = sorted(alarms, key=lambda a: a["timestamp"]) self.settings["alarm"] = alarms
def dump_alarms(self, tag=""): # Useful when debugging dump = "\n" + "="*30 + " ALARMS " + tag + " " + "="*30 + "\n" dump += "raw = " + str(self.settings["alarm"]) + "\n\n" now_ts = to_utc(now_utc()).timestamp() dt = datetime.fromtimestamp(now_ts) dump += "now = {} ({})\n".format( nice_time(self.get_alarm_local(timestamp=now_ts), speech=False, use_ampm=not self.use_24hour, use_24hour = self.use_24hour), now_ts) dump += " U{} L{}\n".format(to_utc(dt), to_local(dt)) dump += "\n\n" idx = 0 for alarm in self.settings["alarm"]: dt = self.get_alarm_local(alarm) dump += "alarm[{}] - {} \n".format(idx, alarm) dump += " Next: {} {}\n".format( nice_time(dt, speech=False, use_ampm= not self.use_24hour, use_24hour = self.use_24hour), nice_date(dt, now=now_local())) dump += " U{} L{}\n".format(dt, to_local(dt)) if 'snooze' in alarm: dtOrig = self.get_alarm_local(timestamp=alarm['snooze']) dump += " Orig: {} {}\n".format( nice_time(dtOrig, speech=False, use_ampm= not self.use_24hour, use_24hour = self.use_24hour), nice_date(dtOrig, now=now_local())) idx += 1 dump += "="*75 self.log.info(dump)
def test_now_utc(self, mock_dt, mock_conf): dt_test = datetime(year=1985, month=10, day=25, hour=8, minute=18) mock_dt.utcnow.return_value = dt_test mock_conf.get.return_value = test_config self.assertEqual(now_utc(), dt_test.replace(tzinfo=gettz('UTC'))) mock_dt.utcnow.assert_called_with()
def has_expired_alarm(self): # True is an alarm should be 'going off' now. Snoozed alarms don't # count until they are triggered again. if not self.settings["alarm"]: return False now_ts = to_utc(now_utc()).timestamp() for alarm in self.settings["alarm"]: if alarm['timestamp'] <= now_ts: return True return False
def waqi_query_and_report(self, city, pollutant): if self.settings.get('APIKey') is not None: reqAQI = requests.get('https://api.waqi.info/feed/' + city + '/?token=' + self.settings.get('APIKey')) objAQI = json.loads(reqAQI.text) if objAQI['status'] == 'ok': try: value = objAQI['data']['iaqi'][lookup[pollutant]]['v'] except: self.speak_dialog('pollutant.not.reported', { 'pollutant': nice_name[pollutant], 'city': city }) else: config = Configuration.get() station_string = objAQI["data"]["city"]["name"] station = station_string.split(',')[0].split('(')[0] utc_time = now_utc( ) # alternatively, set utc_time to datetime.now(timezone.utc) rec_time = datetime.strptime( objAQI["data"]["time"]["s"] + objAQI["data"]["time"]["tz"].split(':')[0] + objAQI["data"]["time"]["tz"].split(':')[1], "%Y-%m-%d %H:%M:%S%z") rec_time = to_utc(rec_time) timediff = (utc_time - rec_time).total_seconds() if timediff // 3600 >= 3: # reading is more than 3 hours old self.dialog( 'Current readings for this location are not available. Please check back another time.' ) else: if city in station: incity = '' else: incity = 'in ' + city self.speak_dialog( 'pollutant.level.is', { 'pollutant': nice_name[pollutant], 'incity': incity, 'value': value, 'station': station }) if nice_name[pollutant] == 'pm 2.5': self.health_advisory(value) elif objAQI['data'] == 'Unknown station': self.speak_dialog('city.not.reported', {'city': city}) elif objAQI['data'] == 'Invalid key': self.speak_dialog('invalid.key', {'city': city}) else: self.speak_dialog('key.not.found')
def on_user_activity(self, message): # When the unit speaks, consider the user active. Queue up a # notice of available update in 30 seconds. self.cancel_scheduled_event('QueueNotice') pause = now_utc() + timedelta(seconds=30) self.schedule_event(self._queue_notice, pause, name='QueueNotice') # Queued to notify now, don't bug again until at least the next day self.remove_event('recognizer_loop:audio_output_end') self.cancel_scheduled_event('DailyVersionCheck') self.schedule_repeating_event( self.daily_version_check, None, # wait to run 60 * 60 * 24, # seconds in a day name='DailyVersionCheck')
def get_local_datetime(self, location, dtUTC=None): if not dtUTC: dtUTC = now_utc() if self.display_tz: # User requested times be shown in some timezone tz = self.display_tz else: tz = self.get_timezone(self.location_timezone) if location: tz = self.get_timezone(location) if not tz: self.speak_dialog("time.tz.not.found", {"location": location}) return None return dtUTC.astimezone(tz)
def initialize(self): # Update allowed version to current version if lower or incorrect current_allowed = self.config_core.get("max_allowed_core_version", 0) core_version_float = CORE_VERSION_MAJOR + CORE_VERSION_MINOR / 10 if (not isinstance(current_allowed, float) or current_allowed < core_version_float): self.save_upgrade_permission( [CORE_VERSION_MAJOR, CORE_VERSION_MINOR]) # Start repeating event once aday to inform of new major version daily = 60 * 60 * 24 # seconds in a day self.schedule_repeating_event( self.daily_version_check, now_utc(), # run asap 60 * 60 * 24, # repeat daily daily, name='DailyVersionCheck')
def _next_repeat(self, alarm): # evaluate recurrence to the next instance if 'snooze' in alarm: # repeat from original time (it was snoozed) ref = datetime.fromtimestamp(alarm["repeat_rule"]) else: ref = datetime.fromtimestamp(alarm["timestamp"]) # Create a repeat rule and get the next alarm occurrance after that start = to_utc(ref) rr = rrulestr("RRULE:" + alarm["repeat_rule"], dtstart=start) now = to_utc(now_utc()) next = rr.after(now) self.log.debug(" Now={}".format(now)) self.log.debug("Original={}".format(start)) self.log.debug(" Next={}".format(next)) return { "timestamp": to_utc(next).timestamp(), "repeat_rule": alarm["repeat_rule"], "name": alarm["name"], }
def handle_query_date(self, message, response_type="simple"): utt = message.data.get('utterance', "").lower() try: extract = extract_datetime(utt) except Exception: self.speak_dialog('date.not.found') return day = extract[0] if extract else now_local() # check if a Holiday was requested, e.g. "What day is Christmas?" year = extract_number(utt) if not year or year < 1500 or year > 3000: # filter out non-years year = day.year all_holidays = {} # TODO: How to pick a location for holidays? for st in holidays.US.STATES: holiday_dict = holidays.US(years=[year], state=st) for d, name in holiday_dict.items(): if name not in all_holidays: all_holidays[name] = d for name in all_holidays: d = all_holidays[name] # Uncomment to display all holidays in the database # self.log.info("Day, name: " +str(d) + " " + str(name)) if name.replace(" Day", "").lower() in utt: day = d break location = self._extract_location(utt) today = to_local(now_utc()) if location: # TODO: Timezone math! if (day.year == today.year and day.month == today.month and day.day == today.day): day = now_utc() # for questions ~ "what is the day in sydney" day = self.get_local_datetime(location, dtUTC=day) if not day: return # failed in timezone lookup speak_date = nice_date(day, lang=self.lang) # speak it if response_type == "simple": self.speak_dialog("date", {"date": speak_date}) elif response_type == "relative": # remove time data to get clean dates day_date = day.replace(hour=0, minute=0, second=0, microsecond=0) today_date = today.replace(hour=0, minute=0, second=0, microsecond=0) num_days = (day_date - today_date).days if num_days >= 0: speak_num_days = nice_duration(num_days * 86400) self.speak_dialog("date.relative.future", { "date": speak_date, "num_days": speak_num_days }) else: # if in the past, make positive before getting duration speak_num_days = nice_duration(num_days * -86400) self.speak_dialog("date.relative.past", { "date": speak_date, "num_days": speak_num_days }) # and briefly show the date self.answering_query = True self.show_date(location, day=day) time.sleep(10) mycroft.audio.wait_while_speaking() if self.platform == "mycroft_mark_1": self.enclosure.mouth_reset() self.enclosure.activate_mouth_events() self.answering_query = False self.displayed_time = None
def handle_set_alarm(self, message): """Handler for "set an alarm for...""" utt = message.data.get('utterance').lower() recur = None if message.data.get('Recurring'): # Just ignoring the 'Recurrence' now, we support more complex stuff # recurrence = message.data.get('Recurrence') recur = self._create_day_set(utt) # TODO: remove days following an "except" in the utt while not recur: r = self.get_response('query.recurrence', num_retries=1) if not r: return recur = self._create_day_set(r) if self.voc_match(utt, "Except"): # TODO: Support exceptions self.speak_dialog("no.exceptions.yet") return # Get the time when, utt_no_datetime = extract_datetime(utt, lang=self.lang) or (None, utt) # Get name from leftover string from extract_datetime name = self._get_alarm_name(utt_no_datetime) # Will return dt of unmatched string today = extract_datetime(self.texts.get('today'), lang=self.lang) today = today[0] # Check the time if it's midnight. This is to check if the user # said a recurring alarm with only the Day or if the user did # specify to set an alarm on midnight. If it's confirmed that # it's for a day only, then get another response from the user # to clarify what time on that day the recurring alarm is. is_midnight = self._check_if_utt_has_midnight(utt, when, self.threshold) if (when is None or when.time() == today.time()) and not is_midnight: r = self.get_response('query.for.when', validator=extract_datetime) if not r: self.speak_dialog("alarm.schedule.cancelled") return when_temp = extract_datetime(r, lang=self.lang) if when_temp is not None: when_temp = when_temp[0] # TODO add check for midnight # is_midnight = self._check_if_utt_has_midnight(r, when_temp, # self.threshold) when = when_temp if when is None \ else datetime(tzinfo=when.tzinfo, year=when.year, month=when.month, day=when.day, hour=when_temp.hour, minute=when_temp.minute) else: when = None # Verify time alarm_time = when confirmed_time = False while (not when or when == today) and not confirmed_time: if recur: t = nice_time(alarm_time, use_ampm=not self.use_24hour, use_24hour = self.use_24hour) conf = self.ask_yesno('confirm.recurring.alarm', data={ 'time': t, 'recurrence': self._recur_desc(recur) }) else: t = nice_date_time(alarm_time, now=today, use_ampm= not self.use_24hour, use_24hour = self.use_24hour, lang=self.lang) conf = self.ask_yesno('confirm.alarm', data={'time': t}) if not conf: return if conf == 'yes': when = [alarm_time] confirmed_time = True else: # check if a new (corrected) time was given when = extract_datetime(conf, lang=self.lang) if when is not None: when = when[0] if not when or when == today: # Not a confirmation and no date/time in statement, quit return alarm_time = when when = None # reverify alarm = {} if not recur: alarm_time_ts = to_utc(alarm_time).timestamp() now_ts = now_utc().timestamp() if alarm_time_ts > now_ts: alarm = self.set_alarm(alarm_time, name) else: if (self.texts.get('today') in utt) or (self.texts.get('tonight') in utt): self.speak_dialog('alarm.past') return else: # Set the alarm to find the next 24 hour time slot while alarm_time_ts < now_ts: alarm_time_ts += 86400.0 alarm_time = datetime.utcfromtimestamp(alarm_time_ts) alarm = self.set_alarm(alarm_time, name) else: alarm = self.set_alarm(alarm_time, name, repeat=recur) if not alarm: # none set, it was a duplicate return # Don't want to hide the animation self.enclosure.deactivate_mouth_events() if confirmed_time: self.speak_dialog("alarm.scheduled") else: t = self._describe(alarm) reltime = nice_relative_time(self.get_alarm_local(alarm), lang=self.lang) if recur: self.speak_dialog("recurring.alarm.scheduled.for.time", data={"time": t, "rel": reltime}) else: self.speak_dialog("alarm.scheduled.for.time", data={"time": t, "rel": reltime}) self._show_alarm_anim(alarm_time) self.enclosure.activate_mouth_events()