feat(sono): madrugada silenciosa e N sons para acordar
Antes um único som acima do limiar, um quadro de movimento ou um pulso do PIR acordavam o rosto; deitado na cama, ele acordava toda hora. - Som: precisa de `somAcordar` barulhos (padrão 3) em 1 minuto; o mesmo barulho comprido conta uma vez. - Madrugada silenciosa (padrão 23h às 7h, configurável): movimento, rastreamento, PIR e modo presença não acordam; só barulho ALTO repetido, toque ou conversa. - Vida: de madrugada não acorda sozinho e volta a dormir mesmo com gente se mexendo, a não ser que alguém tenha interagido nos últimos 10 min. - Painel: grupo "Sono e madrugada" nos Ajustes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -741,6 +741,27 @@ const MIC = {
|
||||
cooldown: parseFloat(par.get("somcooldown") || "5"), // s entre sustos
|
||||
};
|
||||
|
||||
/* Acordar com som: precisa de N barulhos em 1 minuto (ajuste "Sons para acordar"). Na madrugada
|
||||
silenciosa só barulho ALTO conta, e movimento/câmera não acordam (alguém se mexendo na cama). */
|
||||
const DESPERTAR = { sons: 3, janela: 60, noite: true, ini: 23, fim: 7, eventos: [], ultimo: -Infinity, estava: false };
|
||||
function madrugada() {
|
||||
if (!DESPERTAR.noite || DESPERTAR.ini === DESPERTAR.fim) return false;
|
||||
const h = new Date().getHours();
|
||||
return DESPERTAR.ini < DESPERTAR.fim ? h >= DESPERTAR.ini && h < DESPERTAR.fim : h >= DESPERTAR.ini || h < DESPERTAR.fim;
|
||||
}
|
||||
/* Conta o INÍCIO de cada barulho (o mesmo barulho comprido vale um) e diz se já dá para acordar. */
|
||||
function barulhoAcorda(ouviu, agora) {
|
||||
const D = DESPERTAR;
|
||||
if (ouviu && !D.estava && agora - D.ultimo >= 1.5) {
|
||||
D.ultimo = agora;
|
||||
D.eventos = D.eventos.filter((t) => agora - t < D.janela).concat(agora);
|
||||
console.log("ouvido: barulho " + D.eventos.length + "/" + D.sons + (madrugada() ? " (madrugada)" : ""));
|
||||
}
|
||||
D.estava = ouviu;
|
||||
if (D.eventos.length >= D.sons && agora - D.eventos[D.eventos.length - 1] < 1) { D.eventos = []; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
function som(nivel) {
|
||||
const n = Math.max(0, Math.min(1, Number(nivel) || 0));
|
||||
const agora = performance.now() / 1000;
|
||||
@@ -765,12 +786,14 @@ function som(nivel) {
|
||||
console.log("ouvido nivel=" + n.toFixed(2) + " piso=" + (MIC.piso || 0).toFixed(2));
|
||||
}
|
||||
|
||||
if (r.dormindo && temSom) { // acordou: tinha barulho
|
||||
if (r.dormindo) {
|
||||
if (barulhoAcorda(madrugada() ? alto : temSom, agora)) { // acordou: barulho repetido
|
||||
r.dormindo = false;
|
||||
animar("surpreso");
|
||||
MIC.proxSurpresa = agora + MIC.cooldown;
|
||||
console.log("ouvido: acordou nivel=" + n.toFixed(2));
|
||||
relatarSom("acordou", n);
|
||||
}
|
||||
} else if (alto && !MIC.estavaAlto && agora >= MIC.proxSurpresa) {
|
||||
MIC.proxSurpresa = agora + MIC.cooldown; // susto: barulho alto e repentino
|
||||
animar("surpreso");
|
||||
@@ -857,7 +880,7 @@ function rastrear(t) {
|
||||
MOV.olharY+=((t.y-.5)*2*MOV.olharMax*.6-MOV.olharY)*.65;
|
||||
if(t.atividade===true) {
|
||||
MOV.ultimoMov=agora;
|
||||
if(r.dormindo) { r.dormindo=false;animar('surpreso'); }
|
||||
if(r.dormindo && !madrugada()) { r.dormindo=false;animar('surpreso'); }
|
||||
}
|
||||
}
|
||||
function movimento(vel, cx, cy) {
|
||||
@@ -887,12 +910,14 @@ function movimento(vel, cx, cy) {
|
||||
}
|
||||
|
||||
const forte = v >= MOV.limiarForte;
|
||||
if (r.dormindo && mexeu) { // acordou: algo se mexeu na frente
|
||||
if (r.dormindo) {
|
||||
if (mexeu && !madrugada()) { // acordou: algo se mexeu na frente (de dia)
|
||||
r.dormindo = false;
|
||||
animar("surpreso");
|
||||
MOV.proxSurpresa = agora + MOV.cooldown;
|
||||
console.log("presenca: acordou nivel=" + v.toFixed(2));
|
||||
relatarMov("acordou", v);
|
||||
}
|
||||
} else if (forte && !MOV.estavaForte && agora >= MOV.proxSurpresa) {
|
||||
// susto so no INICIO do movimento forte: se continuar se mexendo, nao repete
|
||||
MOV.proxSurpresa = agora + MOV.cooldown;
|
||||
@@ -1040,6 +1065,10 @@ function configurarApp(c) {
|
||||
EXTRAS.boca = c.boca !== false; EXTRAS.sobrancelhas = c.sobrancelhas === true; EXTRAS.iris = c.iris === true;
|
||||
if (typeof c.subirBoca === 'number') EXTRAS.subirBoca = c.subirBoca;
|
||||
SONO.apagar = c.apagarTela !== false;
|
||||
if (typeof c.somAcordar === 'number') DESPERTAR.sons = Math.max(1, Math.round(c.somAcordar));
|
||||
DESPERTAR.noite = c.noiteSilenciosa !== false;
|
||||
if (typeof c.noiteInicio === 'number') DESPERTAR.ini = Math.round(c.noiteInicio);
|
||||
if (typeof c.noiteFim === 'number') DESPERTAR.fim = Math.round(c.noiteFim);
|
||||
if (typeof c.apagarTelaMin === 'number') SONO.minutos = c.apagarTelaMin;
|
||||
if (typeof c.distSobrancelha === 'number') EXTRAS.distSobrancelha = c.distSobrancelha;
|
||||
AUTO = c.auto;
|
||||
|
||||
+32
-3
@@ -741,6 +741,27 @@ const MIC = {
|
||||
cooldown: parseFloat(par.get("somcooldown") || "5"), // s entre sustos
|
||||
};
|
||||
|
||||
/* Acordar com som: precisa de N barulhos em 1 minuto (ajuste "Sons para acordar"). Na madrugada
|
||||
silenciosa só barulho ALTO conta, e movimento/câmera não acordam (alguém se mexendo na cama). */
|
||||
const DESPERTAR = { sons: 3, janela: 60, noite: true, ini: 23, fim: 7, eventos: [], ultimo: -Infinity, estava: false };
|
||||
function madrugada() {
|
||||
if (!DESPERTAR.noite || DESPERTAR.ini === DESPERTAR.fim) return false;
|
||||
const h = new Date().getHours();
|
||||
return DESPERTAR.ini < DESPERTAR.fim ? h >= DESPERTAR.ini && h < DESPERTAR.fim : h >= DESPERTAR.ini || h < DESPERTAR.fim;
|
||||
}
|
||||
/* Conta o INÍCIO de cada barulho (o mesmo barulho comprido vale um) e diz se já dá para acordar. */
|
||||
function barulhoAcorda(ouviu, agora) {
|
||||
const D = DESPERTAR;
|
||||
if (ouviu && !D.estava && agora - D.ultimo >= 1.5) {
|
||||
D.ultimo = agora;
|
||||
D.eventos = D.eventos.filter((t) => agora - t < D.janela).concat(agora);
|
||||
console.log("ouvido: barulho " + D.eventos.length + "/" + D.sons + (madrugada() ? " (madrugada)" : ""));
|
||||
}
|
||||
D.estava = ouviu;
|
||||
if (D.eventos.length >= D.sons && agora - D.eventos[D.eventos.length - 1] < 1) { D.eventos = []; return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
function som(nivel) {
|
||||
const n = Math.max(0, Math.min(1, Number(nivel) || 0));
|
||||
const agora = performance.now() / 1000;
|
||||
@@ -765,12 +786,14 @@ function som(nivel) {
|
||||
console.log("ouvido nivel=" + n.toFixed(2) + " piso=" + (MIC.piso || 0).toFixed(2));
|
||||
}
|
||||
|
||||
if (r.dormindo && temSom) { // acordou: tinha barulho
|
||||
if (r.dormindo) {
|
||||
if (barulhoAcorda(madrugada() ? alto : temSom, agora)) { // acordou: barulho repetido
|
||||
r.dormindo = false;
|
||||
animar("surpreso");
|
||||
MIC.proxSurpresa = agora + MIC.cooldown;
|
||||
console.log("ouvido: acordou nivel=" + n.toFixed(2));
|
||||
relatarSom("acordou", n);
|
||||
}
|
||||
} else if (alto && !MIC.estavaAlto && agora >= MIC.proxSurpresa) {
|
||||
MIC.proxSurpresa = agora + MIC.cooldown; // susto: barulho alto e repentino
|
||||
animar("surpreso");
|
||||
@@ -857,7 +880,7 @@ function rastrear(t) {
|
||||
MOV.olharY+=((t.y-.5)*2*MOV.olharMax*.6-MOV.olharY)*.65;
|
||||
if(t.atividade===true) {
|
||||
MOV.ultimoMov=agora;
|
||||
if(r.dormindo) { r.dormindo=false;animar('surpreso'); }
|
||||
if(r.dormindo && !madrugada()) { r.dormindo=false;animar('surpreso'); }
|
||||
}
|
||||
}
|
||||
function movimento(vel, cx, cy) {
|
||||
@@ -887,12 +910,14 @@ function movimento(vel, cx, cy) {
|
||||
}
|
||||
|
||||
const forte = v >= MOV.limiarForte;
|
||||
if (r.dormindo && mexeu) { // acordou: algo se mexeu na frente
|
||||
if (r.dormindo) {
|
||||
if (mexeu && !madrugada()) { // acordou: algo se mexeu na frente (de dia)
|
||||
r.dormindo = false;
|
||||
animar("surpreso");
|
||||
MOV.proxSurpresa = agora + MOV.cooldown;
|
||||
console.log("presenca: acordou nivel=" + v.toFixed(2));
|
||||
relatarMov("acordou", v);
|
||||
}
|
||||
} else if (forte && !MOV.estavaForte && agora >= MOV.proxSurpresa) {
|
||||
// susto so no INICIO do movimento forte: se continuar se mexendo, nao repete
|
||||
MOV.proxSurpresa = agora + MOV.cooldown;
|
||||
@@ -1040,6 +1065,10 @@ function configurarApp(c) {
|
||||
EXTRAS.boca = c.boca !== false; EXTRAS.sobrancelhas = c.sobrancelhas === true; EXTRAS.iris = c.iris === true;
|
||||
if (typeof c.subirBoca === 'number') EXTRAS.subirBoca = c.subirBoca;
|
||||
SONO.apagar = c.apagarTela !== false;
|
||||
if (typeof c.somAcordar === 'number') DESPERTAR.sons = Math.max(1, Math.round(c.somAcordar));
|
||||
DESPERTAR.noite = c.noiteSilenciosa !== false;
|
||||
if (typeof c.noiteInicio === 'number') DESPERTAR.ini = Math.round(c.noiteInicio);
|
||||
if (typeof c.noiteFim === 'number') DESPERTAR.fim = Math.round(c.noiteFim);
|
||||
if (typeof c.apagarTelaMin === 'number') SONO.minutos = c.apagarTelaMin;
|
||||
if (typeof c.distSobrancelha === 'number') EXTRAS.distSobrancelha = c.distSobrancelha;
|
||||
AUTO = c.auto;
|
||||
|
||||
+14
-1
@@ -22,6 +22,7 @@ PADRAO = dict(preview=False, auto=True, audio=True, volume=0.35,
|
||||
economia=True, brilho=0.7, brilhoRepouso=0.08, repousoTela=30,
|
||||
boca=True, sobrancelhas=False, iris=False, subirBoca=0, distSobrancelha=16, videoRTC=True,
|
||||
apagarTela=True, apagarTelaMin=5,
|
||||
somAcordar=3, noiteSilenciosa=True, noiteInicio=23, noiteFim=7,
|
||||
sensorMic=True, sensorCamera=True, micSono=True, cameraSono=False,
|
||||
cameraLado='user', videoResolucao='auto', videoFPS=0,
|
||||
rastrearRosto=True, rastrearMaos=True, seguirRastreamento=True, mostrarRastreamento=True)
|
||||
@@ -32,7 +33,8 @@ LIMITES = dict(volume=(0, 1), somlim=(0, 1), somganho=(0, 1),
|
||||
somaltolim=(0, 1), sompausa=(1, 3600), somcooldown=(0, 120),
|
||||
movlim=(0, 1), movforte=(0, 1), movcooldown=(0, 120), movolhar=(0, 100),
|
||||
brilho=(0.01, 1), brilhoRepouso=(0.01, 1), repousoTela=(5, 3600),
|
||||
subirBoca=(-20, 60), distSobrancelha=(0, 60), apagarTelaMin=(1, 120))
|
||||
subirBoca=(-20, 60), distSobrancelha=(0, 60), apagarTelaMin=(1, 120),
|
||||
somAcordar=(1, 20), noiteInicio=(0, 23), noiteFim=(0, 23))
|
||||
|
||||
|
||||
def validar(dados):
|
||||
@@ -54,6 +56,17 @@ def validar(dados):
|
||||
return dados
|
||||
|
||||
|
||||
def madrugada(config, agora=None):
|
||||
"""Dentro do horário da madrugada silenciosa (ex.: 23h às 7h, atravessando a meia-noite)?"""
|
||||
if not config.get('noiteSilenciosa', True):
|
||||
return False
|
||||
ini, fim = int(config.get('noiteInicio', 23)), int(config.get('noiteFim', 7))
|
||||
hora = time.localtime(agora if agora is not None else time.time()).tm_hour
|
||||
if ini == fim:
|
||||
return False
|
||||
return ini <= hora < fim if ini < fim else (hora >= ini or hora < fim)
|
||||
|
||||
|
||||
def ip_informado(dados):
|
||||
"""IP que o próprio app informa (aparelho.ip); o endereço da conexão pode ser de um proxy."""
|
||||
aparelho = dados.get('aparelho')
|
||||
|
||||
+6
-3
@@ -32,7 +32,7 @@ from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
|
||||
from urllib.parse import unquote
|
||||
from pathlib import Path
|
||||
from comum import autorizado, carregar_token, gravar_atomico, ler_json
|
||||
from controle_pc import ControlePC, versao
|
||||
from controle_pc import ControlePC, madrugada, versao
|
||||
from eventos import Eventos
|
||||
from servos import ControleServos
|
||||
from braco import Braco
|
||||
@@ -505,13 +505,15 @@ def main():
|
||||
vida.celular_dormindo = celular_dormindo
|
||||
vida.celular_online = lambda: servidor.controle.resumo()['online']
|
||||
vida.alguem_perto = alguem_perto
|
||||
vida.madrugada = lambda agora: madrugada(servidor.controle.config, agora)
|
||||
# E-Stop parado, ou modo presença com ninguém em casa (dormindo em silêncio): não faz nada
|
||||
vida.bloqueado = lambda: ESTOP["ativo"] or servidor.historico.presenca.get("estado") == "ausente"
|
||||
|
||||
def presenca(tipo):
|
||||
vida.estimulo(tipo)
|
||||
# PIR viu alguém: se o rosto estiver dormindo (e talvez com a tela apagada), acorda
|
||||
if tipo == "pir":
|
||||
# PIR viu alguém: se o rosto estiver dormindo (e talvez com a tela apagada), acorda.
|
||||
# De madrugada não: alguém se mexendo na cama não é motivo para acordar.
|
||||
if tipo == "pir" and not madrugada(servidor.controle.config):
|
||||
with servidor.controle.lock:
|
||||
dormindo = bool(servidor.controle.estado.get("dormindo"))
|
||||
if dormindo:
|
||||
@@ -523,6 +525,7 @@ def main():
|
||||
|
||||
def alguem_chegou():
|
||||
camera(True) # modo presença: o PIR liga a câmera do celular
|
||||
if not madrugada(servidor.controle.config):
|
||||
enfileirar("acordar")
|
||||
|
||||
def ninguem_por_perto():
|
||||
|
||||
+16
-4
@@ -69,12 +69,24 @@ class VidaTest(unittest.TestCase):
|
||||
self.v.necessidades['energia'] = 100
|
||||
self.assertEqual(self.v.agir(DIA + 120), 'acordar')
|
||||
|
||||
def test_noite_dorme_sem_ninguem(self):
|
||||
def test_madrugada_dorme_mesmo_com_gente_se_mexendo(self):
|
||||
noite = time.mktime((2026, 10, 6, 23, 30, 0, 0, 0, -1))
|
||||
self.v.alguem_perto = lambda: True # deitado na cama, o PIR vê
|
||||
self.assertEqual(self.v.agir(noite), 'dormir')
|
||||
self.v.dormindo_proprio = False
|
||||
self.v.alguem_perto = lambda: True
|
||||
self.assertNotEqual(self.v.agir(noite), 'dormir')
|
||||
self.v.necessidades['energia'] = 100
|
||||
self.assertIsNone(self.v.agir(noite + 60)) # descansado, mas é madrugada: não acorda
|
||||
self.v.estimulo('carinho', noite + 120) # carinho acorda e segura acordado um tempo
|
||||
self.assertFalse(self.v.dormindo())
|
||||
self.assertNotEqual(self.v.agir(noite + 180), 'dormir')
|
||||
self.assertEqual(self.v.agir(noite + 180 + 11 * 60), 'dormir')
|
||||
|
||||
def test_horario_da_madrugada(self):
|
||||
from controle_pc import madrugada
|
||||
h = lambda hora: time.mktime((2026, 10, 6, hora, 10, 0, 0, 0, -1))
|
||||
cfg = dict(noiteSilenciosa=True, noiteInicio=23, noiteFim=7)
|
||||
self.assertEqual([madrugada(cfg, h(x)) for x in (22, 23, 3, 6, 7, 12)], [False, True, True, True, False, False])
|
||||
self.assertTrue(madrugada(dict(cfg, noiteInicio=1, noiteFim=5), h(2)))
|
||||
self.assertFalse(madrugada(dict(cfg, noiteSilenciosa=False), h(3)))
|
||||
|
||||
def test_humor_e_gestos_de_carente_com_gente_perto(self):
|
||||
self.v.necessidades.update(energia=80, social=10)
|
||||
|
||||
@@ -25,7 +25,8 @@ SONO_LENTO = 0.25
|
||||
RITMOS = {'calmo': (60, 150), 'normal': (25, 75), 'agitado': (12, 35)} # segundos entre atos
|
||||
|
||||
PADRAO = dict(ligado=True, criar_emojis=True, max_emojis_dia=3, ritmo='normal', noite=True)
|
||||
NOITE = (23, 7) # dorme das 23h às 7h, se ninguém estiver por perto
|
||||
NOITE = (23, 7) # horário padrão da madrugada (o servidor usa o dos ajustes do celular)
|
||||
PEDIU_ATENCAO = 10 * 60 # de madrugada, só fica acordado se alguém interagiu (carinho, conversa) há pouco
|
||||
INTERVALO_EMOJI = 90 * 60 # no mínimo 90 min entre duas figurinhas criadas sozinho
|
||||
|
||||
# O que cada estímulo faz com as necessidades (e o gesto que sai na hora, se houver).
|
||||
@@ -100,6 +101,8 @@ class Vida:
|
||||
self.alguem_perto = lambda: False # PIR/movimento recente
|
||||
self.bloqueado = lambda: False # E-Stop ou modo presença com ninguém em casa
|
||||
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
|
||||
|
||||
agora = time.time()
|
||||
self.config = dict(PADRAO)
|
||||
@@ -251,6 +254,8 @@ class Vida:
|
||||
self.necessidades[k] = _limitar(self.necessidades[k] + v)
|
||||
if tipo in CUIDADOS:
|
||||
self.anotar('cuidado', CUIDADOS[tipo], agora)
|
||||
if tipo in CUIDADOS or tipo == 'conversa':
|
||||
self.interagiu_em = agora # alguém quis ele acordado (sensor não conta)
|
||||
if gesto and not self.bloqueado():
|
||||
if self.dormindo():
|
||||
self.acordar(agora, None)
|
||||
@@ -271,7 +276,8 @@ class Vida:
|
||||
if motivo:
|
||||
self.anotar('sono', motivo, agora)
|
||||
|
||||
def noite(self, agora):
|
||||
@staticmethod
|
||||
def noite_padrao(agora):
|
||||
hora = time.localtime(agora).tm_hour
|
||||
ini, fim = NOITE
|
||||
return hora >= ini or hora < fim
|
||||
@@ -340,12 +346,12 @@ class Vida:
|
||||
n = self.necessidades
|
||||
perto = self.alguem_perto()
|
||||
if self.dormindo():
|
||||
# acorda descansado de dia, ou sozinho quando a energia enche e tem alguém por perto
|
||||
if n['energia'] >= 95 and (not self.noite(agora) or perto):
|
||||
# de madrugada nunca acorda sozinho (nem com gente se mexendo na cama); de dia, quando descansou
|
||||
if n['energia'] >= 95 and not self.madrugada(agora):
|
||||
self.acordar(agora, 'Acordou descansado')
|
||||
return 'acordar'
|
||||
return None
|
||||
if self.config['noite'] and self.noite(agora) and not perto:
|
||||
if self.config['noite'] and self.madrugada(agora) and agora - self.interagiu_em > PEDIU_ATENCAO:
|
||||
self.dormir(agora, 'Ficou tarde, foi dormir')
|
||||
return 'dormir'
|
||||
if n['energia'] < 20:
|
||||
|
||||
@@ -55,7 +55,7 @@ export const GRUPOS: { titulo: string; descricao: string; campos: Campo[] }[] =
|
||||
{ chave: 'economia', rotulo: 'Reduzir brilho em repouso', tipo: 'toggle' },
|
||||
{ chave: 'brilhoRepouso', rotulo: 'Brilho em repouso', tipo: 'slider', min: 0.01, max: 1, passo: 0.01 },
|
||||
{ chave: 'repousoTela', rotulo: 'Espera para reduzir brilho', tipo: 'slider', min: 5, max: 300, passo: 5, unidade: ' s' },
|
||||
{ chave: 'apagarTela', rotulo: 'Apagar a tela quando dormir', ajuda: 'Tela preta e brilho mínimo; acorda com som, movimento, toque ou PIR', tipo: 'toggle' },
|
||||
{ chave: 'apagarTela', rotulo: 'Apagar a tela quando dormir', ajuda: 'Tela preta e brilho mínimo; acorda com som repetido, movimento, toque ou PIR', tipo: 'toggle' },
|
||||
{ chave: 'apagarTelaMin', rotulo: 'Dormindo há quanto tempo para apagar', tipo: 'slider', min: 1, max: 120, passo: 1, unidade: ' min' },
|
||||
],
|
||||
},
|
||||
@@ -73,6 +73,21 @@ export const GRUPOS: { titulo: string; descricao: string; campos: Campo[] }[] =
|
||||
{ chave: 'movolhar', rotulo: 'Quanto os olhos acompanham', tipo: 'slider', min: 0, max: 100, passo: 1, unidade: '%' },
|
||||
],
|
||||
},
|
||||
{
|
||||
titulo: 'Sono e madrugada',
|
||||
descricao: 'O que acorda o rosto enquanto ele dorme.',
|
||||
campos: [
|
||||
{ chave: 'somAcordar', rotulo: 'Sons para acordar', ajuda: 'Quantos barulhos em 1 minuto para ele acordar', tipo: 'slider', min: 1, max: 10, passo: 1 },
|
||||
{
|
||||
chave: 'noiteSilenciosa',
|
||||
rotulo: 'Madrugada silenciosa',
|
||||
ajuda: 'Movimento, câmera e PIR não acordam; só barulho alto repetido, toque ou conversa',
|
||||
tipo: 'toggle',
|
||||
},
|
||||
{ chave: 'noiteInicio', rotulo: 'Começa às', tipo: 'slider', min: 0, max: 23, passo: 1, unidade: 'h' },
|
||||
{ chave: 'noiteFim', rotulo: 'Termina às', tipo: 'slider', min: 0, max: 23, passo: 1, unidade: 'h' },
|
||||
],
|
||||
},
|
||||
{
|
||||
titulo: 'Vídeo e rastreamento',
|
||||
descricao: 'Qualidade da transmissão e rastreamento feito neste PC.',
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
function idade(d: number) {
|
||||
return d === 0 ? 'nasceu hoje' : d === 1 ? '1 dia de vida' : `${d} dias de vida`;
|
||||
}
|
||||
const horarioNoite = $derived.by(() => {
|
||||
const c = estado.appConfig;
|
||||
const ini = typeof c?.noiteInicio === 'number' ? c.noiteInicio : 23;
|
||||
const fim = typeof c?.noiteFim === 'number' ? c.noiteFim : 7;
|
||||
return `Das ${ini}h às ${fim}h. Gente se mexendo não acorda; carinho e conversa sim`;
|
||||
});
|
||||
const detalheFigurinhas = $derived(
|
||||
v ? [`${v.emojis_hoje} de ${v.config.max_emojis_dia} hoje`, MOTIVOS[v.pode_criar] ?? v.pode_criar].filter(Boolean).join(' · ') : '',
|
||||
);
|
||||
@@ -104,7 +110,7 @@
|
||||
</span>
|
||||
{/snippet}
|
||||
</Linha>
|
||||
<Linha icone={Moon} cor="#5e5ce6" rotulo="Dormir à noite" detalhe="Das 23h às 7h, se não tiver ninguém por perto">
|
||||
<Linha icone={Moon} cor="#5e5ce6" rotulo="Dormir à noite" detalhe={horarioNoite}>
|
||||
{#snippet acessorio()}
|
||||
<Interruptor rotulo="Dormir à noite" checked={v.config.noite} onchange={(l) => configurarVida({ noite: l })} />
|
||||
{/snippet}
|
||||
|
||||
Reference in New Issue
Block a user