feat(presenca): sensibilidade do PIR por confirmação (padrão média)
O HC-SR505 não tem ajuste físico e disparava por pouco (57 disparos numa hora de madrugada). Agora só conta como presença o disparo confirmado: média = 2 em 1 min ou ligado direto 12 s; baixa = 3 em 90 s ou 20 s; alta = qualquer um. A bolinha do Ao vivo segue o pino cru. Ajuste em Eventos › Modo presença. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+46
-7
@@ -21,7 +21,11 @@ from comum import gravar_atomico, ler_json
|
||||
|
||||
TIPOS = ('pir', 'movimento', 'som')
|
||||
PADRAO = dict(pir=True, movimento=True, som=True, foto=True, intervalo=15, dias=3,
|
||||
presenca=True, ausencia=5) # modo presença: o PIR liga a câmera; sem ninguém, ela desliga
|
||||
presenca=True, ausencia=5, # modo presença: o PIR liga a câmera; sem ninguém, ela desliga
|
||||
sensibilidade_pir='media')
|
||||
# O HC-SR505 não tem ajuste de sensibilidade e segura ~8 s a cada disparo. Para separar alguém de
|
||||
# verdade de um disparo à toa: (disparos que bastam, dentro de quantos s, ou ligado direto por quantos s).
|
||||
REGRAS_PIR = {'alta': (1, 0, 0), 'media': (2, 60, 12), 'baixa': (3, 90, 20)}
|
||||
MAX_EVENTOS, MAX_FOTOS = 2000, 500 # teto de segurança; quem manda é a retenção em dias
|
||||
VALIDADE_PEDIDO = 12 # s que o app tem para mandar a foto
|
||||
|
||||
@@ -51,6 +55,8 @@ class Historico:
|
||||
self.pir = dict(disponivel=False, erro='', linha=os.environ.get('CARINHA_PIR', 'gpiochip1:70'), ultimo=None,
|
||||
ativo=False) # ativo = pino em nível alto agora (o HC-SR505 segura ~8 s e rearma)
|
||||
self.processo_pir = None
|
||||
self.subidas_pir = [] # disparos ainda não confirmados (horários)
|
||||
self.subiu_em = None # quando o pino subiu da última vez (ligado direto desde então)
|
||||
with self.db() as db:
|
||||
db.execute('CREATE TABLE IF NOT EXISTS eventos(id INTEGER PRIMARY KEY, horario REAL, tipo TEXT, evento TEXT, nivel REAL, foto TEXT)')
|
||||
self.limpar_antigos()
|
||||
@@ -82,6 +88,9 @@ class Historico:
|
||||
elif chave == 'dias':
|
||||
if isinstance(valor, bool) or not isinstance(valor, int) or not 1 <= valor <= 30:
|
||||
raise ValueError('Guardar de 1 a 30 dias')
|
||||
elif chave == 'sensibilidade_pir':
|
||||
if valor not in REGRAS_PIR:
|
||||
raise ValueError('Sensibilidade do sensor: alta, media ou baixa')
|
||||
elif chave == 'intervalo':
|
||||
if isinstance(valor, bool) or not isinstance(valor, (int, float)) or not 0 <= valor <= 600:
|
||||
raise ValueError('Intervalo entre 0 e 600 s')
|
||||
@@ -134,21 +143,51 @@ class Historico:
|
||||
self.publicar('pir', dict(ultimo=self.pir['ultimo'], ativo=self.pir['ativo'], presenca=dict(self.presenca)))
|
||||
|
||||
def nivel_pir(self, alto, agora=None, registrar=True):
|
||||
"""Mudança no pino do PIR. Subida = presença (evento + foto); enquanto alto, a bolinha fica acesa."""
|
||||
"""Mudança no pino do PIR. A bolinha acompanha o pino; presença (evento, foto, acordar) só
|
||||
quando o disparo se confirma pela sensibilidade escolhida."""
|
||||
agora = agora if agora is not None else time.time()
|
||||
self.pir['ativo'] = alto
|
||||
if alto:
|
||||
self.pir['ultimo'] = agora
|
||||
self.marcar_presenca(agora)
|
||||
self.avisar_pir()
|
||||
if alto and registrar:
|
||||
self.registrar('pir', 'presença')
|
||||
if not alto:
|
||||
self.checar_pir(agora) # ligado direto até agora pode já ter confirmado
|
||||
self.subiu_em = None
|
||||
return
|
||||
if not registrar: # pino assentando ao ligar: só a bolinha
|
||||
return
|
||||
quantos, janela, _ = REGRAS_PIR[self.config['sensibilidade_pir']]
|
||||
with self.lock:
|
||||
self.subidas_pir = [t for t in self.subidas_pir if agora - t < janela] + [agora]
|
||||
self.subiu_em = agora
|
||||
confirmou = len(self.subidas_pir) >= quantos
|
||||
if confirmou:
|
||||
self.confirmar_pir(agora)
|
||||
|
||||
def checar_pir(self, agora=None):
|
||||
"""Ligado direto por bastante tempo = alguém se mexendo na frente (o sensor foi redisparando)."""
|
||||
agora = agora if agora is not None else time.time()
|
||||
_, _, segurar = REGRAS_PIR[self.config['sensibilidade_pir']]
|
||||
with self.lock:
|
||||
pronto = segurar and self.subiu_em is not None and self.subidas_pir and agora - self.subiu_em >= segurar
|
||||
if pronto:
|
||||
self.confirmar_pir(agora)
|
||||
|
||||
def confirmar_pir(self, agora):
|
||||
with self.lock:
|
||||
self.subidas_pir = []
|
||||
self.marcar_presenca(agora)
|
||||
self.registrar('pir', 'presença', agora=agora)
|
||||
|
||||
def vigiar_ausencia(self):
|
||||
passo = 0
|
||||
while True:
|
||||
time.sleep(10)
|
||||
time.sleep(1)
|
||||
passo += 1
|
||||
try:
|
||||
self.checar_ausencia()
|
||||
self.checar_pir()
|
||||
if passo % 10 == 0:
|
||||
self.checar_ausencia()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user