feat(vida): ritmo também define a sensibilidade dos sensores

O servidor manda "ritmo calmo|normal|agitado" ao celular junto com o
humor, e o rosto ajusta limiares e quantos estímulos precisa:

- calmo: limiares 50% mais altos, 3 sustos seguidos para reagir, o dobro
  de sons e 4 movimentos para acordar, 3 disparos do PIR em 2 min;
- normal: reage a barulho alto e movimento forte, 2 movimentos ou os
  sons configurados para acordar;
- agitado: limiares 40% mais baixos, reage a qualquer som ou movimento
  (com reações variadas) e acorda no primeiro estímulo.

A madrugada silenciosa continua valendo por cima. Com o modo vida
desligado o celular volta ao "normal" (só os Ajustes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
julio
2026-10-06 17:39:14 -03:00
co-authored by Claude Opus 5.5
parent dd40c730be
commit 9e09df40e2
6 changed files with 141 additions and 52 deletions
+19 -1
View File
@@ -23,6 +23,9 @@ RECUPERA_SONO = 2.5 # energia por minuto dormindo (25 -> 100 em ~30 min
SONO_LENTO = 0.25
RITMOS = {'calmo': (60, 150), 'normal': (25, 75), 'agitado': (12, 35)} # segundos entre atos
# O ritmo também é a sensibilidade: o celular recebe "ritmo x" e ajusta limiares e quantos estímulos
# precisa para reagir/acordar. Aqui, quantos disparos do PIR em 2 min acordam o rosto.
PIR_ACORDAR = {'calmo': 3, 'normal': 1, 'agitado': 1}
PADRAO = dict(ligado=True, criar_emojis=True, max_emojis_dia=3, ritmo='normal', noite=True)
NOITE = (23, 7) # horário padrão da madrugada (o servidor usa o dos ajustes do celular)
@@ -103,6 +106,7 @@ class Vida:
self.pode_gemini = lambda: bool(os.environ.get('GEMINI_API_KEY'))
self.madrugada = self.noite_padrao # o main troca pelo horário dos ajustes
self.interagiu_em = 0.0
self.pir_eventos = []
agora = time.time()
self.config = dict(PADRAO)
@@ -160,8 +164,11 @@ class Vida:
self.anotar('vida', 'Modo autônomo ligado' if limpo['ligado'] else 'Modo autônomo desligado')
if not limpo['ligado']:
self.enfileirar('humor normal') # o celular volta aos gestos soltos de sempre
self.humor_enviado = (None, 0.0)
self.enfileirar('ritmo normal') # e à sensibilidade dos Ajustes
self.proximo_ato = time.time() + 5
if self.config['ligado'] and 'ritmo' in limpo:
self.enfileirar('ritmo ' + self.config['ritmo'])
self.humor_enviado = (None, 0.0)
self.salvar()
self.avisar()
return self.status()
@@ -276,6 +283,16 @@ class Vida:
if motivo:
self.anotar('sono', motivo, agora)
def pir_acorda(self, agora=None):
"""Disparo do PIR com o rosto dormindo: acorda? No ritmo calmo precisa de vários em 2 min."""
agora = agora or time.time()
self.pir_eventos = [t for t in self.pir_eventos if agora - t < 120] + [agora]
precisa = PIR_ACORDAR[self.config['ritmo']] if self.config['ligado'] else 1
if len(self.pir_eventos) >= precisa:
self.pir_eventos = []
return True
return False
@staticmethod
def noite_padrao(agora):
hora = time.localtime(agora).tm_hour
@@ -388,6 +405,7 @@ class Vida:
# o celular escolhe os gestos soltos pelo humor; reenvia de tempos em tempos (pode ter reiniciado)
if humor != self.humor_enviado[0] or agora - self.humor_enviado[1] > 60:
self.enfileirar('humor ' + humor)
self.enfileirar('ritmo ' + self.config['ritmo'])
self.humor_enviado = (humor, agora)
if agora >= self.proximo_ato:
ato = self.agir(agora)