# Decisiones técnicas

## 2026-09-10 — Alcance de la base

Se sigue el paso 1 acordado en la conversación: estructura, configuración
documentada y backend verificable. La sección «Primera tarea para Codex»
del documento original es una propuesta más amplia; sus entregables de
WebSocket, vistas mock y esquema completo no forman parte de este paso.

## Backend inicial

PHP 8.3, sin framework ni dependencias para este endpoint. Front controller
en `backend/public/index.php`; respuestas en `backend/src/http.php`.
No se selecciona todavía un framework definitivo para la aplicación.
API `/api/v1`, errores JSON, timestamps UTC y respuestas sin caché.
`health` es una comprobación de proceso, no de disponibilidad de dependencias.

## Configuración y publicación

La fase actual no consume secretos. Las plantillas preparan los nombres
de variables; el cargador se implementará al introducir persistencia.
En producción, suministrar secretos desde configuración fuera del directorio
público. Nunca usar la raíz del repositorio como DocumentRoot.
La raíz incluye una denegación Apache porque está bajo el htdocs compartido.
La efectividad de `.htaccess` depende de AllowOverride; el despliegue deberá
garantizar la restricción en la configuración del virtual host.

## Propuestas para la fase 1 (todavía no implementadas)

- Sesiones PHP en servidor, cookie HttpOnly/Secure/SameSite y protección CSRF.
- PDO MySQL, consultas preparadas y migraciones SQL numeradas con registro
  de versiones aplicadas; sin ORM inicialmente.
- Membresía y rol comprobados en backend en cada acceso al hogar.
- IDs internos enteros; claves únicas de membresía `(home_id, user_id)`.
- Integridad de relaciones por hogar también al enlazar habitaciones y dispositivos.
- HTML/CSS/JavaScript con prioridad móvil; PWA posterior.

## Decisiones pendientes antes del dispositivo simulado

- Versión soportada de Node y biblioteca WebSocket, verificadas al instalarlas.
- Canal interno autenticado PHP ↔ Node y responsabilidad de persistencia.
- Política de heartbeat, reconexión y conexiones duplicadas.
- Caducidad de comandos, deduplicación y diferencia entre ACK y resultado.
- Retención e índices de telemetría; secretos de dispositivos y rotación.
- Automatizaciones locales/cloud, OTA y notificaciones quedan para sus fases.

## 2026-09-11 — Implementación de usuarios y hogares

Se concretan PHP sin framework, PDO MySQL y migraciones SQL versionadas.
Sesiones PHP con CSRF para todas las mutaciones, incluido login/registro;
cookies HttpOnly/SameSite y Secure fuera de desarrollo. Se configura una
carpeta privada de sesiones porque la carpeta CLI por defecto de Bitnami
pertenece a daemon. La configuración se inyecta mediante variables de
entorno; no se interpreta automáticamente un archivo .env.

Se añade una instancia MariaDB independiente para desarrollo, sin TCP y
con datos fuera de htdocs. No se obtienen credenciales de otros proyectos.
Compartir un hogar agrega cuentas existentes; invitaciones por correo y
aceptación quedan pendientes. Residentes/guest solo consultan en esta fase.
La pertenencia y los permisos se consultan en cada solicitud, de modo que
quitar un miembro revoca también el acceso desde sesiones abiertas.

La limitación de autenticación persiste por hash de IP en MariaDB (30
solicitudes/15 minutos, registro y login combinados). Pendiente para
publicación: recuperación/verificación de correo, política de limpieza
periódica de auth_attempts y ajuste de límites según uso real.

## Usuarios únicos y perfiles infantiles

Se añade `username` global único (3–32 caracteres ASCII, empieza con letra),
normalizado a minúsculas; email pasa a ser opcional. Las cuentas anteriores
conservan correo/contraseña y pueden elegir su usuario en Mi usuario.
Login y vinculación aceptan usuario o correo. Se mantiene compatibilidad
con clientes anteriores que envían `email` al login o al agregar miembros.

El propietario crea perfiles infantiles sin correo para un solo hogar.
Son cuentas de tipo `child` permanente en esta fase, con rol infantil;
no se pueden promover ni vincular desde los endpoints de miembros normales.
El acceso inicial a habitaciones se deniega salvo selección explícita.
Las habitaciones seleccionadas deben pertenecer al hogar administrado.
El propietario puede cambiar nombre/usuario, contraseña, habitaciones y
pausar/reactivar. Cada edición incrementa auth_version para invalidar las
sesiones anteriores. Solo consulta: sin configuración, miembros ni creación
de hogares. Los controles de dispositivos todavía no existen.

La migración 002 usa sintaxis de MariaDB (`ADD COLUMN IF NOT EXISTS`),
probada en la instancia instalada. Antes de usar MySQL, adaptar y verificar
esta sintaxis. Se mantiene copia de seguridad privada antes de migrar los
datos de desarrollo existentes. No se crean cuentas infantiles de muestra.

## Monitoreo ESP32 — transporte y datos

Se implementa un único servicio Node 24 LTS (`ws` y `mysql2`, versiones
exactas en package-lock.json), instalado para HomeCore fuera de htdocs.
Escucha en 127.0.0.1:8080; Apache terminará TLS para /ws. El dispositivo
inicia la conexión, se autentica con UID y secreto aleatorio de 256 bits;
solo se almacena SHA-256 del secreto. Se muestra al propietario una sola
vez al registrar o regenerar. Pausa/rotación revocan la conexión persistida.

PHP administra hogares, dispositivos y permisos. Node solo autentica las
conexiones de dispositivos y persiste presencia/telemetría mediante PDO's
esquema compartido, usando mysql2 y transacciones; no se crea un endpoint
HTTP interno ni se comparten sesiones de residentes. Antes de introducir
comandos se decidirá el canal PHP→Node. Un bloqueo de MariaDB asegura un
solo proceso de transporte por base. Cada socket posee una conexión con
identificador único que evita escrituras de sesiones sustituidas.

Telemetría v1 limitada a temperatura y humedad, con mensajes de máximo
4 KiB, ACK tras persistencia y deduplicación por dispositivo/message_id.
La hora de recepción UTC del servidor es autoritativa; no se aceptan cargas
históricas arbitrarias en esta etapa. Se conserva historial por 30 días con
script explícito de mantenimiento. La app consulta cada 5 segundos y
muestra separado estado de conexión y antigüedad de la lectura. Los datos
simulados siempre se identifican. Niños: filtro obligatorio de habitación
en listado, última lectura e historial; ningún permiso de configuración.

## Piloto en transbook.mx/homecore

El usuario eligió un subdirectorio del dominio existente, no un subdominio.
HOMECORE_BASE_PATH adapta rutas de API, recursos y cookie; no depende del
Host ni de un encabezado de usuario. Publicación por Apache/FastCGI con
PHP-FPM exclusivo y proxy WebSocket; el servidor integrado PHP sigue siendo
solo de desarrollo. Se habilitaron servicios systemd para mantener el piloto.
No se inicializa Git hasta que el usuario lo solicite.

La placa confirmada es DOIT ESP32 DevKit V1 (ESP-WROOM-32). Firmware Arduino
core 3.3.11, WebSockets 2.7.2, ArduinoJson 7.4.3, con CA ISRG Root X1 incluida
como certificado público. Cadena TLS actual de Transbook verificada contra
esa CA. Se comienza sin sensor físico; DHT22 queda como opción configurable.

Referencias consultadas:
- https://nodejs.org/dist/index.json (rama 24 LTS)
- https://github.com/websockets/ws (límites, eventos y autenticación)
- https://sidorares.github.io/node-mysql2/docs (pool y transacciones)
- https://httpd.apache.org/docs/2.4/mod/mod_proxy.html (Upgrade y FastCGI)
- https://www.php.net/manual/en/install.fpm.configuration.php
- https://docs.espressif.com/projects/arduino-esp32/en/latest/installing.html
- https://github.com/Links2004/arduinoWebSockets
- https://letsencrypt.org/certificates/ (cadena de confianza)

## Corrección de compatibilidad de la placa física

Los accesos reales de arduino-WebSocket-Client recibían 403 porque la
biblioteca WebSockets 2.7.2 incluye Origin: file:// por defecto. Se admite
ese valor exacto, además de la ausencia de Origin; otros orígenes se
rechazan. La autenticación UID/token permanece obligatoria y probada,
incluso cuando se presenta file://. No requiere modificar el firmware.

## ARD-360 confirmado: DHT11

Steren identifica ARD-360 como DHT11; usuario confirma cuatro patitas sin
placa adicional. Se introduce HOMECORE_SENSOR_TYPE: 0 simulado, 11 DHT11,
22 DHT22. Configuraciones anteriores siguen funcionando y el selector nuevo
tiene prioridad. GPIO27 para datos, 3V3 y resistencia externa de 10 kΩ.
No hay cambios de API o credenciales. Source sensor identifica nuevas
lecturas reales; el historial simulado permanece marcado. Fallos de lectura
o valores fuera del rango ARD-360 se omiten, nunca se reemplazan por datos
simulados. Se distribuye un ZIP específico con ejemplo de configuración11.
Fuente: https://www.steren.com.mx/sensor-de-temperatura-y-humedad.html

## Alertas de sensores — septiembre 2026

Por solicitud del usuario se implementan reglas y registro web antes de decidir
si habrá aplicación nativa. No se añade PWA, push ni proveedor externo.
Motor PHP independiente mediante timer systemd, con progreso transaccional en
MariaDB y eventos persistentes, separado del transporte WebSocket. Se reutilizan
credenciales DML de PHP. Permite evaluar falta de lecturas aun sin tráfico ni
navegadores abiertos y evita acoplar reglas a sesiones del dispositivo.

Se implementan tres reglas opcionales por dispositivo: temperatura, humedad,
falta de lecturas reales. Propietario configura, miembros consultan conforme a
sus permisos actuales; niños filtrados por habitación en backend. Una alerta
por episodio, resolución automática, sin recordatorios repetidos. Configurar
reinicia evaluación; no se imponen umbrales a los sensores existentes. Detalles,
limitaciones y operación en `docs/alerts.md`.

## Control LED — septiembre 2026

Usuario solicita misma ESP32 con DHT11 GPIO27, tira monocolor 12V y MOSFET
IRLB8721 en GPIO23. Se agrega salida digital encendido/apagado, sin PWM.
Capacidad declarada por firmware, comandos absolutos con TTL 15s, identidad
ligada a conexión, ACK y estado reportado. Cola SQL evita puerto interno nuevo.
Primer control restringido al propietario; niños conservan solo consulta.
Apagado al arranque/pérdida detectada, sin restaurar encendido automáticamente.
Detalle técnico en docs/led-control.md; limitación eléctrica a 3.3V documentada
en la guía de firmware y comunicada al usuario antes de proceder con código.

## Dispositivos del hogar separados de la ESP32 — 2026-09-23

Se añaden entidades room_devices: sensor climático y tira LED como dispositivos
con identidad, nombre y componentes propios, agrupados por habitación. Se conserva
el controlador devices y su protocolo para evitar cambiar credenciales, firmware,
telemetría y comandos ya comprobados por el usuario. La habitación se hereda del
controlador en esta fase; no se añade permiso de actuación infantil. Datos de
componentes provienen del usuario y son editables por propietario. Diseño, API y
límites en docs/room-devices.md. Sin dependencias nuevas.

## Ubicación independiente de canales — 2026-09-23

Usuario confirma mover tira y sensor entre habitaciones de un mismo hogar.
Migración 007 añade room_id a room_devices y conserva ubicación inicial.
PATCH exclusivo de ubicación validado por propietario/hogar evita sobrescribir
fichas. Telemetría, alertas y listados infantiles usan habitación del canal,
redactando campos de canales no autorizados en respuestas del controlador.
La conexión física, reglas e historial no cambian. Pruebas cubren cambio
individual, acceso infantil perdido/ganado, hogares ajenos, fichas y móvil.

## PIR local con transición PWM — 2026-09-23

Usuario acepta HC-SR501 todavía sin cablear, espera30s y transición2s. GPIO26
libre; se conserva MOSFET GPIO23 y DHT GPIO27. Opt-in por HOMECORE_PIR_ENABLED,
compatible con firmware LED anterior. Motor local en tarea FreeRTOS para que
TLS bloqueante no congele temporización ni fade. Warmup60s, espera desde OUT
bajo (retardo físico adicional), NVS para modo/ajustes, salida apagada al arranque.
Automático funciona offline; manual prioriza botones hasta activar auto de nuevo.
Cloud confirma configuración mediante ACK exacto; no declara ejecutado antes
ni mide luz física. Información PIR se autoriza por habitación del canal LED.
Se documentó que pausar conexión no detiene automatización local.

## Diagnóstico PIR — 2026-09-30

Ante falta de detección, lectura del piloto confirmó esp32-pir-1, estabilizado,
conectado, último movimiento0 y modo Manual. No se cambia remotamente el modo
ni se afirma avería física. Código lee GPIO26 cada20ms; Manual inhibe actuación
pero no detección. Se añade diagnóstico Serial2s con GPIO crudo, uptime, ready,
movimiento, modo y brillo, versión esp32-pir-2. Sin cambios en protocolo o motor.
El firmware conserva ajustes NVS, incluidos Manual y tiempos previos.

## Reinicios del piloto PIR v2 — octubre 2026

Registro enviado: assertion udp_new_ip_type (TCPIP core lock), después watchdog
CPU1 alrededor de primera lectura DHT tras autenticación. Sin ELF exacto del
usuario no se decodifican direcciones usando un ELF distinto ni se afirma
causa definitiva. PIR v3 cambia configTime por esp_netif_sntp_init tras obtener
IP, interfaz recomendada de ESP-NETIF. Añade HOMECORE_TEMPERATURE_ENABLED,
defecto0 en PIR para aislar sensor DHT desconectado; no envía datos simulados.
No desactiva watchdogs, TLS, mutex ni estabilización60s. Motor local sin cambios.
Requiere validar estabilidad física tras nueva carga.

Usuario solicita volver a incluir DHT11: se restaura TEMPERATURE_ENABLED=1
por defecto y en el ejemplo del paquete PIR, manteniendo SNTP mediante
ESP-NETIF. Una copia previa de secrets.h con valor0 requiere cambiarlo a1.
Compilar no confirma solución del watchdog físico; se necesita registro con
DHT conectado y lecturas habilitadas para validar estabilidad.

## Activación explícita de modo automático — 2026-10-05

Usuario reporta guardar automático pero recibe MANUAL. Auditoría de cola mostró
que las dos solicitudes recientes contenían mode=manual y fueron confirmadas;
no se atribuye la causa al firmware ni al usuario sin reproducción. Se añade
botón directo Activar automático, que fija mode=auto conservando tiempos/brillo.
Confirmación ahora incluye modo y consulta el estado reportado después del ACK,
para no presentar un mensaje genérico ante un modo diferente. Prueba móvil
parte de estado manual y verifica activación explícita y formulario existente.
No cambia firmware ni fuerza actuación física desde pruebas.
