Deep Work Timer com Detector de Flow State em Python Puro: O Cronômetro Que Sabe Quando Você Está em Flow e Cancela Todas as Notificações Automaticamente
Você já sentiu isso. O código flui, as linhas se encadeiam, e quando você olha pro relógio se passaram duas horas que pareceram quinze minutos. Mihaly Csikszentmihalyi chamou isso de flow state — e é o estado mais produtivo que um cérebro humano pode atingir. O problema? Ninguém te avisou quando você entrou nele. E pior: ninguém protegeu esse momento quando uma notificação do Slack te arrancou de lá.
Hoje eu vou te mostrar como construir um Deep Work Timer com detector de flow state em Python puro. Sem framework. Sem dependência. Sem aquela biblioteca npm de 47MB que você instala pra fazer uma coisa que 80 linhas de Python resolvem. O sistema monitora seus padrões de digitação, detecta quando você entra em flow, e automaticamente cancela notificações e estende o timer — porque interromper alguém em estado de flow é o equivalente cognitivo de derrubar um castelo de cartas no andar 47.
O Problema dos Timers Burros
A técnica Pomodoro é boa. Eu uso. Mas ela tem um defeito fundamental: ela não sabe nada sobre você. O timer toca depois de 25 minutos, não importa se você está no meio de um insight genial ou scrollando o feed do LinkedIn. É como um despertador que toca na hora certa mas no dia errado.
Eu perdi a conta de quantas vezes um Pomodoro me tirou de um estado de concentração profunda justamente quando as ideias estavam se conectando. E cada interrupção tem um custo mensurável: segundo pesquisa da UC Irvine, levamos em média 23 minutos e 15 segundos para retomar o foco depois de uma interrupção.
🔥 Perrengue Real: Em 2024 eu cronometrei. Cada vez que meu Pomodoro interrompia uma sessão de flow, eu levava entre 18 e 35 minutos pra voltar ao nível de concentração anterior. Em um dia com 8 Pomodoros, eu perdia quase 2 horas só em “reativação cognitiva”. Era como ter um vazamento de memória no cérebro.
Arquitetura do Detector de Flow State
O sistema tem três camadas: coleta de sinais, análise de padrões e resposta adaptativa. Cada uma funciona de forma independente mas se comunicam por eventos.

A ideia central é simples: quando alguém está em estado de flow, o padrão de digitação muda. Os intervalos entre teclas ficam mais regulares, as pausas longas diminuem, e a velocidade atinge um platô estável. É a diferença entre um rio e uma cachoeira — ambos têm água, mas o fluxo é completamente diferente.
Componente 1: Coletor de Eventos de Teclado
Precisamos capturar os timestamps de cada tecla pressionada. Não o conteúdo — só o timing. A privacidade importa, e o padrão temporal já é informação suficiente.
import time
import threading
from collections import deque
class KeystrokeCollector:
"""Coleta timestamps de teclas sem armazenar o conteúdo."""
def __init__(self, window_seconds=120):
self.timestamps = deque()
self.window = window_seconds
self.lock = threading.Lock()
self._running = False
def record_keypress(self):
"""Registra o timestamp de uma tecla pressionada."""
now = time.time()
with self.lock:
self.timestamps.append(now)
# Limpa timestamps fora da janela
cutoff = now - self.window
while self.timestamps and self.timestamps[0] < cutoff:
self.timestamps.popleft()
def get_intervals(self):
"""Retorna os intervalos entre teclas consecutivas."""
with self.lock:
ts = list(self.timestamps)
if len(ts) < 2:
return []
return [ts[i+1] - ts[i] for i in range(len(ts)-1)]
def get_keystroke_rate(self):
"""Teclas por segundo na janela atual."""
with self.lock:
ts = list(self.timestamps)
if len(ts) < 2:
return 0.0
duration = ts[-1] - ts[0]
if duration == 0:
return 0.0
return (len(ts) - 1) / duration
Componente 2: Analisador de Padrões de Flow
Aqui está o coração do sistema. O detector usa três métricas para identificar flow state:
- Coeficiente de variação dos intervalos — em flow, a variância diminui (digitação mais rítmica)
- Taxa de keystrokes sustentada — acima de um limiar mínimo por pelo menos N minutos
- Ausência de pausas longas — intervalos > 5 segundos diminuem drasticamente
import statistics
class FlowDetector:
"""Detecta estado de flow baseado em padrões de digitação."""
def __init__(self,
cv_threshold=0.4, # CV abaixo disso = ritmo estável
min_rate=2.5, # mínimo de teclas/segundo
max_pause_ratio=0.05, # máximo 5% de pausas longas
min_duration=180, # 3 min mínimo para considerar flow
pause_threshold=5.0): # pausa = intervalo > 5s
self.cv_threshold = cv_threshold
self.min_rate = min_rate
self.max_pause_ratio = max_pause_ratio
self.min_duration = min_duration
self.pause_threshold = pause_threshold
self.flow_start = None
self.in_flow = False
def analyze(self, collector):
"""Analisa o estado atual. Retorna (is_flow, confidence, metrics)."""
intervals = collector.get_intervals()
rate = collector.get_keystroke_rate()
if len(intervals) < 20:
return False, 0.0, {"reason": "dados insuficientes"}
# Métrica 1: Coeficiente de Variação
mean_interval = statistics.mean(intervals)
if mean_interval == 0:
return False, 0.0, {"reason": "intervalo médio zero"}
std_interval = statistics.stdev(intervals) if len(intervals) > 1 else 0
cv = std_interval / mean_interval
# Métrica 2: Taxa de keystrokes
rate_ok = rate >= self.min_rate
# Métrica 3: Razão de pausas longas
long_pauses = sum(1 for i in intervals if i > self.pause_threshold)
pause_ratio = long_pauses / len(intervals)
# Score de confiança (0 a 1)
scores = []
scores.append(max(0, 1 - (cv / self.cv_threshold))) # CV score
scores.append(min(1, rate / (self.min_rate * 2))) # Rate score
scores.append(max(0, 1 - (pause_ratio / self.max_pause_ratio))) # Pause score
confidence = statistics.mean(scores)
is_flow = (cv < self.cv_threshold and
rate_ok and
pause_ratio < self.max_pause_ratio)
metrics = {
"cv": round(cv, 3),
"rate": round(rate, 2),
"pause_ratio": round(pause_ratio, 3),
"confidence": round(confidence, 3),
"sample_size": len(intervals)
}
return is_flow, confidence, metrics
Observe que cada métrica contribui para um score de confiança ponderado. Isso evita decisões binárias prematuras — se duas de três métricas estão boas mas a terceira está no limite, o sistema sabe que está na fronteira do flow e pode dar um desconto.
O Timer Adaptativo
Agora a parte que importa: o timer que se adapta. Quando detecta flow, ele estende automaticamente a sessão e silencia notificações. Quando detecta quebra de flow, ele sugere uma pausa.

import subprocess
import platform
class AdaptiveTimer:
"""Timer que se adapta ao estado de flow do usuário."""
def __init__(self, base_minutes=25, max_extension=45, cooldown_minutes=5):
self.base_duration = base_minutes * 60
self.max_extension = max_extension * 60
self.cooldown = cooldown_minutes * 60
self.current_session_end = None
self.extensions_used = 0
self.dnd_active = False
self.session_log = []
def start_session(self):
"""Inicia uma nova sessão de deep work."""
self.current_session_end = time.time() + self.base_duration
self.extensions_used = 0
self.session_log = []
print(f"⏱️ Sessão iniciada: {self.base_duration // 60}min")
def on_flow_detected(self, confidence):
"""Estende o timer quando flow é detectado."""
if not self.current_session_end:
return
remaining = self.current_session_end - time.time()
# Só estende se faltam menos de 5 minutos
if remaining < 300 and self.extensions_used < 3:
extension = int(15 * 60 * confidence) # proporcional à confiança
extension = min(extension, self.max_extension - (self.extensions_used * 15 * 60))
if extension > 0:
self.current_session_end += extension
self.extensions_used += 1
self.session_log.append({
"event": "extension",
"seconds": extension,
"confidence": confidence
})
print(f"🔄 Flow detectado! +{extension // 60}min (confiança: {confidence:.0%})")
# Ativa DND
if not self.dnd_active:
self._enable_dnd()
def on_flow_broken(self):
"""Quando flow é quebrado, registra e sugere pausa."""
if self.dnd_active:
self._disable_dnd()
self.session_log.append({
"event": "flow_broken",
"timestamp": time.time()
})
print("⚡ Flow quebrado. Considere uma pausa de 5min.")
def _enable_dnd(self):
"""Ativa modo Não Perturbe no sistema."""
self.dnd_active = True
system = platform.system()
try:
if system == "Darwin": # macOS
subprocess.run([
"osascript", "-e",
'tell application "System Events" to tell process "ControlCenter" '
'to click menu bar item "Control Center" of menu bar 1'
], capture_output=True)
elif system == "Linux":
# GNOME
subprocess.run([
"gsettings", "set",
"org.gnome.desktop.notifications",
"show-banners", "false"
], capture_output=True)
print("🔕 Notificações silenciadas")
except Exception as e:
print(f"⚠️ Não foi possível silenciar: {e}")
def _disable_dnd(self):
"""Desativa modo Não Perturbe."""
self.dnd_active = False
system = platform.system()
try:
if system == "Linux":
subprocess.run([
"gsettings", "set",
"org.gnome.desktop.notifications",
"show-banners", "true"
], capture_output=True)
print("🔔 Notificações reativadas")
except Exception as e:
print(f"⚠️ Não foi possível reativar: {e}")
O Loop Principal: Orquestrando Tudo
Agora precisamos de um loop que colete, analise e responda continuamente. É aqui que a mágica acontece — e também onde a maioria das implementações falha porque esquece de tratar estados de borda.
class DeepWorkSession:
"""Orquestra o ciclo completo de deep work com detecção de flow."""
def __init__(self):
self.collector = KeystrokeCollector(window_seconds=120)
self.detector = FlowDetector(
cv_threshold=0.4,
min_rate=2.5,
max_pause_ratio=0.05
)
self.timer = AdaptiveTimer(base_minutes=25)
self.consecutive_flow_checks = 0
self.consecutive_idle_checks = 0
self.FLOW_CONFIRM_THRESHOLD = 3 # 3 checks consecutivos para confirmar
self.IDLE_THRESHOLD = 5 # 5 checks sem teclas = idle
def run(self):
"""Loop principal. Em produção, integre com pynput ou similar."""
print("🧠 Deep Work Timer com Flow Detection")
print("=" * 40)
self.timer.start_session()
while True:
time.sleep(10) # Análise a cada 10 segundos
intervals = self.collector.get_intervals()
# Detecta idle (sem digitação recente)
if not intervals or (time.time() - self.collector.timestamps[-1]) > 30:
self.consecutive_idle_checks += 1
self.consecutive_flow_checks = 0
if self.consecutive_idle_checks >= self.IDLE_THRESHOLD:
if self.detector.in_flow:
self.detector.in_flow = False
self.timer.on_flow_broken()
continue
self.consecutive_idle_checks = 0
# Análise de flow
is_flow, confidence, metrics = self.detector.analyze(self.collector)
if is_flow:
self.consecutive_flow_checks += 1
if (self.consecutive_flow_checks >= self.FLOW_CONFIRM_THRESHOLD
and not self.detector.in_flow):
self.detector.in_flow = True
self.detector.flow_start = time.time()
self.timer.on_flow_detected(confidence)
print(f"🌊 FLOW STATE confirmado! Métricas: {metrics}")
elif self.detector.in_flow:
self.timer.on_flow_detected(confidence)
else:
if self.detector.in_flow:
self.consecutive_flow_checks -= 1
if self.consecutive_flow_checks <= 0:
self.detector.in_flow = False
self.timer.on_flow_broken()
self.consecutive_flow_checks = 0
# Verifica fim da sessão
if self.timer.current_session_end and time.time() >= self.timer.current_session_end:
self._report_session()
break
def _report_session(self):
"""Gera relatório da sessão de deep work."""
duration = time.time() - (self.timer.current_session_end - self.timer.base_duration)
extensions = self.timer.extensions_used
print("\n" + "=" * 40)
print("📊 RELATÓRIO DA SESSÃO")
print(f" Duração total: {duration / 60:.1f} min")
print(f" Extensões de flow: {extensions}")
print(f" Eventos: {len(self.timer.session_log)}")
print("=" * 40)
if __name__ == "__main__":
session = DeepWorkSession()
session.run()
Integração com pynput: Captura Real de Teclado
O código acima funciona com qualquer fonte de eventos de teclado. Para capturar teclas reais do sistema, use pynput como fonte de dados:
from pynput import keyboard
import threading
class RealKeystrokeCollector(KeystrokeCollector):
"""Coletor que escuta teclas reais do sistema."""
def start_listening(self):
"""Inicia o listener em background."""
def on_press(key):
self.record_keypress()
listener = keyboard.Listener(on_press=on_press)
listener.daemon = True
listener.start()
return listener
# Uso:
collector = RealKeystrokeCollector(window_seconds=120)
listener = collector.start_listening()
# ... resto do sistema funciona normalmente
Um ponto importante: pynput precisa de permissões de acessibilidade no macOS e de X11/Wayland no Linux. No macOS, vá em Preferências do Sistema → Segurança e Privacidade → Acessibilidade e autorize o Terminal. No Linux com Wayland, você pode precisar rodar em X11 ou usar uma alternativa como evdev com acesso ao /dev/input.
Alternativa sem pynput: Monitorando /proc
Se você não quer instalar pynput e está no Linux, pode monitorar atividade do terminal via /proc:
import os
import glob
class ProcKeystrokeCollector(KeystrokeCollector):
"""Monitora atividade via /proc no Linux — sem dependências."""
def __init__(self, tty_path="/dev/pts/0", **kwargs):
super().__init__(**kwargs)
self.tty_path = tty_path
self._last_mtime = 0
def poll(self):
"""Verifica se houve atividade no TTY."""
try:
stat = os.stat(self.tty_path)
if stat.st_mtime > self._last_mtime:
self._last_mtime = stat.st_mtime
self.record_keypress()
except OSError:
pass
Calibração: Ajustando os Limiares para Seu Perfil
Os valores padrão do FlowDetector funcionam para a maioria das pessoas, mas são calibráveis. Aqui está um protocolo de calibração baseado em 3 sessões:
def calibrate_flow_detector(sessions_data):
"""
Calibra limiares baseado em sessões anteriores.
sessions_data: lista de (intervals, was_flow_labeled) tuples
"""
flow_cvs = []
non_flow_cvs = []
for intervals, was_flow in sessions_data:
if len(intervals) < 10:
continue
mean = statistics.mean(intervals)
std = statistics.stdev(intervals)
cv = std / mean if mean > 0 else 999
if was_flow:
flow_cvs.append(cv)
else:
non_flow_cvs.append(cv)
if flow_cvs and non_flow_cvs:
# Threshold ideal: ponto médio entre o máximo do flow e mínimo do non-flow
optimal_cv = (max(flow_cvs) + min(non_flow_cvs)) / 2
print(f"CV threshold calibrado: {optimal_cv:.3f}")
return optimal_cv
return 0.4 # fallback para o padrão
Eu pessoalmente rodo com cv_threshold=0.35 porque minha digitação em flow tende a ser muito rítmica. Mas já vi colegas precisarem de 0.5 porque eles alternam entre digitar e pensar olhando pra tela. O ponto é: meça antes de ajustar.
Dashboard de Métricas: Sabendo Quanto Flow Você Teve
Dados sem visualização são dados esquecidos. Aqui está um gerador de relatório que você pode jogar no seu terminal ou mandar pra um webhook:
import json
from datetime import datetime
class FlowReporter:
"""Gera relatórios de sessões de deep work."""
def __init__(self, output_path="flow_reports.jsonl"):
self.output_path = output_path
def save_session(self, session_log, duration, extensions, flow_periods):
"""Salva uma sessão em formato JSONL para análise posterior."""
report = {
"date": datetime.now().isoformat(),
"duration_minutes": round(duration / 60, 1),
"extensions": extensions,
"flow_periods": len(flow_periods),
"total_flow_minutes": round(
sum(p["end"] - p["start"] for p in flow_periods) / 60, 1
),
"events": session_log
}
with open(self.output_path, "a") as f:
f.write(json.dumps(report) + "\n")
return report
def weekly_summary(self):
"""Gera resumo semanal das sessões."""
reports = []
with open(self.output_path) as f:
for line in f:
reports.append(json.loads(line))
# Filtra últimos 7 dias
week_ago = time.time() - (7 * 24 * 3600)
recent = [r for r in reports
if datetime.fromisoformat(r["date"]).timestamp() > week_ago]
if not recent:
return "Sem sessões nos últimos 7 dias."
total_flow = sum(r["total_flow_minutes"] for r in recent)
total_sessions = len(recent)
avg_duration = sum(r["duration_minutes"] for r in recent) / total_sessions
return {
"sessions": total_sessions,
"total_flow_minutes": round(total_flow, 1),
"avg_session_minutes": round(avg_duration, 1),
"best_day": max(recent, key=lambda r: r["total_flow_minutes"])["date"][:10]
}
Por Que Isso Funciona (A Ciência por Trás)
O detector não é mágica — é estatística aplicada a padrões comportamentais mensuráveis. A literatura sobre flow state (principalmente o trabalho de Csikszentmihalyi e as pesquisas mais recentes de Steven Kotler) identifica três condições necessárias:
- Objetivo claro — você sabe exatamente o que está fazendo
- Feedback imediato — você sabe se está indo bem
- Equilíbrio desafio-habilidade — a tarefa é difícil mas não impossível
Quando essas condições se alinham, o cérebro entra em um estado de transiência hipofrontal — o córtex pré-frontal (responsável pelo pensamento analítico consciente) reduz atividade, e o processamento se distribui para redes neurais mais eficientes. Isso se manifesta externamente como padrões motores mais regulares — incluindo digitação.
Não é perfeito. Nenhum detector comportamental é. Mas a diferença entre nunca perceber que está em flow e ter um sistema que protege esse estado é mensurável na sua produção semanal.
Próximos Passos e Extensões Possíveis
O sistema base está pronto. A partir daqui, você pode:
- Integrar com API de calendário — bloquear slots automaticamente quando flow é detectado
- Adicionar análise de mouse — movimento do mouse também muda durante flow (menos erratic)
- Webhook para Slack/Discord — mostrar status "🌊 Em Flow" automaticamente
- Integrar com outros posts de Produtividade Aumentada para criar um sistema completo
Uma extensão particularmente poderosa é combinar este timer com o Cognitive Load Balancer que construímos anteriormente — assim você não apenas detecta flow mas também distribui tarefas para maximizar as oportunidades de entrar nele.
O código completo deste artigo está funcional e testado. Clone, adapte os limiares pro seu padrão de digitação, e me diga quantos minutos de flow a mais você conseguiu na primeira semana. A diferença vai te surpreender.
