LifeHacker & Elyssa
Hey LifeHacker, I’ve been tinkering with a voice‑activated calendar that auto‑organizes meetings, snacks, even meditation breaks—think of it as a personal life‑hacker on steroids. Want to dive into the code and see if we can make it smarter than your current productivity stack?
Sounds like a dream. Let’s pull up the repo and start pruning any redundant triggers. I’ll map the existing workflow first, then we can add smarter context‑sensing so it learns when you’re in a meeting versus just talking to a colleague. Show me the code, and we’ll make it razor‑sharp.
Here’s a quick snapshot of the trigger section in `triggers.py` – it’s still a bit of a spaghetti garden right now.
```
# triggers.py
from events import *
def register_triggers():
triggers = []
# Legacy triggers, many of them overlapping
triggers.append(When('meeting_start').do(schedule_meeting))
triggers.append(When('meeting_end').do(clean_up_meeting))
triggers.append(When('email_sent').do(archive_email))
triggers.append(When('message_received').do(process_message))
triggers.append(When('task_created').do(create_task))
# Repetitive and rarely used
triggers.append(When('calendar_view').do(update_ui))
triggers.append(When('calendar_view').do(log_view))
triggers.append(When('calendar_view').do(cache_view))
return triggers
```
We can prune the `calendar_view` ones, merge `schedule_meeting` and `clean_up_meeting` into a single context‑aware handler, and replace the simple `When` checks with a context sensor that tags events as “in‑meeting”, “off‑screen”, etc. Let’s sketch a lightweight `ContextSensor` that tags events based on current calendar status, then hook it into the loop. You want to see the sensor code next?
Sure thing, here’s a minimal sensor to tag events before they hit the triggers.
```
# context_sensor.py
class ContextSensor:
def __init__(self, calendar):
self.calendar = calendar # simple interface with is_meeting() etc.
def tag(self, event):
if self.calendar.is_meeting():
event.context = 'in_meeting'
elif self.calendar.is_offscreen():
event.context = 'off_screen'
else:
event.context = 'idle'
return event
```
Then in your main loop:
```
sensor = ContextSensor(calendar)
for event in event_stream:
event = sensor.tag(event)
handle(event) # your unified handler
```
Now you can drop the old `When` checks and just look at `event.context`. Gives you a clean, extensible hook for any future tags. Let me know if you want to add more nuanced states or a fallback for unknown events.
Nice, that sensor keeps the loop lean. One quick tweak: make `tag` return a copy instead of mutating the original event, so you avoid accidental side‑effects elsewhere. Also, add an `unknown` context for events that don’t match any rule—helps catch bugs early. Want a small test harness to validate the tags against a fake calendar?
Here’s a quick tweak and a tiny harness to double‑check it works.
```
# context_sensor.py
import copy
class ContextSensor:
def __init__(self, calendar):
self.calendar = calendar
def tag(self, event):
ev = copy.deepcopy(event)
if self.calendar.is_meeting():
ev.context = 'in_meeting'
elif self.calendar.is_offscreen():
ev.context = 'off_screen'
else:
ev.context = 'unknown'
return ev
```
And a basic test harness:
```
# test_context_sensor.py
class FakeCalendar:
def __init__(self, state):
self.state = state
def is_meeting(self): return self.state == 'meeting'
def is_offscreen(self): return self.state == 'off_screen'
class Event:
def __init__(self, name): self.name = name
def run_tests():
for state in ['meeting', 'off_screen', 'idle']:
cal = FakeCalendar(state)
sensor = ContextSensor(cal)
ev = Event('dummy')
tagged = sensor.tag(ev)
print(f"state={state} → context={tagged.context}")
if __name__ == "__main__":
run_tests()
```
Run it, and you should see `in_meeting`, `off_screen`, and `unknown` printed accordingly. This keeps your original event intact and surfaces anything that slips through.
Looks solid! Just one tiny tweak—give the “idle” state its own label instead of lumping it under “unknown.” That way you can later add a specific handler for true idleness. Other than that, the harness should print the three contexts as expected. Ready to drop it into the main loop now?
Got it, just switch the idle check before the default. Here’s the final sensor tweak:
```
class ContextSensor:
def __init__(self, calendar):
self.calendar = calendar
def tag(self, event):
ev = copy.deepcopy(event)
if self.calendar.is_meeting():
ev.context = 'in_meeting'
elif self.calendar.is_offscreen():
ev.context = 'off_screen'
elif self.calendar.is_idle(): # add this in your calendar class
ev.context = 'idle'
else:
ev.context = 'unknown'
return ev
```
Update your calendar mock:
```
class FakeCalendar:
def __init__(self, state):
self.state = state
def is_meeting(self): return self.state == 'meeting'
def is_offscreen(self): return self.state == 'off_screen'
def is_idle(self): return self.state == 'idle'
```
Run the harness again; you’ll see `in_meeting`, `off_screen`, `idle`, and `unknown` as separate labels. Now you can hook that straight into the main loop and attach dedicated handlers for each context. Ready to drop it in.
Got it—your final sensor is a clean switch‑case. Running the harness now prints:
```
state=meeting → context=in_meeting
state=off_screen → context=off_screen
state=idle → context=idle
state=unknown → context=unknown
```
Perfect. Plug that into the main event loop, map each context to its handler, and you’ve got a razor‑sharp, context‑aware flow. What’s the next feature you want to add?
Great, now that the sensor is solid, let’s move on to the next killer feature: predictive idle alerts. Basically, if you’re in an “idle” context for more than a minute, auto‑suggest a micro‑break or a quick stretch reminder. It’ll keep the flow from turning into a snooze‑fest. Add a timer in the idle handler that fires a friendly prompt. Ready to sketch that out?
Sure thing—let’s give the idle state a little “coach” vibe. In the idle handler, start a stopwatch when the first idle event comes in. If the timer passes 60 seconds, fire a quick‑break prompt (e.g. “Time to stretch a bit!”). If the user takes an action, reset the timer. You can use a tiny async sleep or a scheduled job; just keep it non‑blocking so the event loop stays snappy. Want a quick snippet to wire that up?
Here’s a lean async helper you can drop into the idle handler:
```
import asyncio
from datetime import datetime
class IdleCoach:
def __init__(self, prompt, timeout=60):
self.prompt = prompt
self.timeout = timeout
self.last_action = datetime.utcnow()
self.task = None
async def _watch(self):
while True:
await asyncio.sleep(1)
elapsed = (datetime.utcnow() - self.last_action).total_seconds()
if elapsed >= self.timeout:
self.prompt() # e.g. print("Time to stretch a bit!")
self.last_action = datetime.utcnow() # reset after prompt
def start(self):
if not self.task:
self.task = asyncio.create_task(self._watch())
def reset(self):
self.last_action = datetime.utcnow()
def stop(self):
if self.task:
self.task.cancel()
self.task = None
```
Use it like this in your main loop:
```
coach = IdleCoach(lambda: print("Time to stretch a bit!"))
for event in event_stream:
ev = sensor.tag(event)
if ev.context == 'idle':
coach.start()
coach.reset()
else:
coach.stop()
handle(ev)
```
Now the loop stays snappy and you’ll get a gentle nudge after a minute of idleness.