# VMS-Sailor · Brief para Claude Code · Parte 5 de 6 ## VMS-Sailor Mobile (app nativa iOS y Android) > Esta parte detalla la app móvil. Asume que ya leíste Partes 1-4. --- ## 1. Propósito App nativa que permite al **Admin del buque** (owner), técnicos y tripulación autorizados ver y operar el VMS desde tablets y smartphones. Diseñada para uso a bordo, no remoto desde tierra. ### Casos de uso principales - **Owner en cubierta o camarote**: revisar estado general del buque sin ir al puente - **Owner durante navegación**: ajustar trim desde el flybridge mientras conduce - **Técnico durante mantenimiento**: ver lecturas en tiempo real estando físicamente en sala de máquinas - **Tripulación**: recibir notificaciones de alarmas en el teléfono cuando no están en estación fija - **Owner en muelle**: revisar estado del buque amarrado desde su casa cercana (siempre que esté en WiFi local) --- ## 2. Restricciones críticas de seguridad ### Solo WiFi local del buque La app **JAMÁS** se expone a internet. El servidor del Runtime escucha solo en la subred WiFi local (192.168.x.x típicamente). La app móvil descubre el servidor por mDNS/Bonjour o IP fija configurada al enrolar. **Implicaciones:** - No se requiere certificado SSL público (TLS interno con CA propia) - Sin cuentas en la nube, sin datos saliendo del buque - Si la WiFi del buque está apagada, la app no funciona - Si el dueño quiere acceso remoto desde tierra → debe pasar por VPN del armador (instalación separada, no parte de este proyecto) ### Autenticación TOTP + biométrico Doble factor obligatorio: 1. **Algo que sabes**: usuario + password 2. **Algo que tienes**: el dispositivo enrolado (TOTP de 30s) 3. **Algo que eres**: biométrico del dispositivo (FaceID, TouchID, huella Android) — antes de revelar el TOTP ### Enrollment controlado Solo el Admin del buque (owner) puede autorizar nuevos dispositivos. Proceso: 1. Owner en su estación principal del Runtime → "Gestión de dispositivos móviles" 2. Crea nuevo dispositivo: nombre (ej: "iPhone Carlos"), usuario asociado 3. Sistema genera QR con secreto TOTP único 4. Usuario escanea con la app VMS-Sailor Mobile en su dispositivo 5. App pide configurar biométrico local 6. Dispositivo queda enrolado 7. Owner puede revocar en cualquier momento desde la estación ### Permissives respetados igual que estación fija Los permissives se evalúan en el SERVIDOR, no en el cliente. Móvil y desktop son solo UIs. Si un permissive bloquea una acción, bloquea desde cualquier UI. No hay forma de saltarse permissives desde móvil. --- ## 3. Arquitectura técnica ### Stack tecnológico | Componente | Tecnología | |---|---| | Framework | Flutter 3.x | | Lenguaje | Dart 3.x | | State management | Riverpod 2.x | | Comunicación | WebSocket (web_socket_channel) + HTTP/REST (dio) | | TOTP | `otp` package | | Biométrico | `local_auth` | | Almacenamiento seguro | `flutter_secure_storage` (Keychain iOS, Keystore Android) | | Gráficas | `fl_chart` | | SVG (mímicos) | `flutter_svg` | | Notificaciones locales | `flutter_local_notifications` | | Cliente HTTPS con CA propia | configurar en `dio` con `IOHttpClientAdapter` | ### Por qué Flutter (no React Native ni nativo puro) - **Performance gráfico superior**: motor Skia/Impeller propio. Para mímicos con muchas lecturas refrescándose en tiempo real, esto es decisivo. RN sufre con UIs densas y actualizaciones rápidas - **Un solo código para iOS + Android + posible escritorio**: a futuro se puede tener "VMS-Sailor Mobile Lite" como app de escritorio liviana con el mismo código - **Coherencia visual**: misma UI en iPhone, iPad y Android. Flutter renderiza por sí mismo, no usa widgets nativos - **Dart bien diseñado**: tipado fuerte, async/await nativo, null safety. Reduce bugs runtime en sistema crítico - **Soporte enterprise**: Google + BMW + Toyota lo usan en industria ### Estructura de carpetas Flutter ``` mobile/ ├── pubspec.yaml ├── README.md ├── android/ ├── ios/ │ └── lib/ ├── main.dart ├── app.dart # Material/Cupertino app │ ├── core/ │ ├── models/ │ │ ├── tag.dart │ │ ├── alarm.dart │ │ ├── system.dart │ │ ├── permissive.dart │ │ └── user.dart │ ├── api/ │ │ ├── ws_client.dart # Cliente WebSocket │ │ ├── rest_client.dart # Cliente REST │ │ └── auth_client.dart # Auth + TOTP │ ├── auth/ │ │ ├── totp_manager.dart │ │ ├── biometric_gate.dart # local_auth wrapper │ │ └── secure_storage.dart # secretos en Keychain/Keystore │ ├── discovery/ │ │ └── mdns_finder.dart # Encuentra servidor en WiFi │ └── theme/ │ ├── light_theme.dart │ └── dark_theme.dart │ ├── features/ │ ├── enrollment/ │ │ ├── qr_scanner.dart # Escanea QR del Runtime │ │ └── enrollment_flow.dart │ ├── login/ │ │ ├── login_screen.dart │ │ └── biometric_unlock.dart │ ├── overview/ │ │ └── overview_screen.dart # Dashboard inicial │ ├── systems/ │ │ ├── system_list.dart # Lista de sistemas activos │ │ ├── mimic_view.dart # Mímico de un sistema │ │ └── tag_detail.dart │ ├── alarms/ │ │ ├── alarm_list.dart │ │ ├── alarm_detail.dart │ │ └── alarm_notifications.dart │ ├── trends/ │ │ └── trends_screen.dart # Gráficas con fl_chart │ ├── trim/ │ │ ├── trim_control_screen.dart # Panel destacado de trim │ │ ├── trim_slider_widget.dart │ │ ├── stability_indicator.dart # Roll/pitch en vivo │ │ └── emergency_reset_button.dart │ ├── logbook/ │ │ ├── logbook_view.dart │ │ └── manual_entry.dart │ └── settings/ │ └── settings_screen.dart │ └── widgets/ # Widgets reutilizables ├── tag_card.dart ├── alarm_badge.dart ├── gauge_widget.dart ├── lamp_indicator.dart ├── bar_graph.dart └── permissive_status.dart ``` --- ## 4. Pantallas principales ### Login - Detecta si hay servidor VMS-Sailor en la WiFi local (mDNS) - Si no, pide IP manual - Login con usuario + password - Pide biométrico para revelar TOTP - Verifica TOTP contra servidor - Si OK, guarda token de sesión ### Overview (dashboard inicial) Lo primero que ve el usuario después de loguear: - Nombre del buque + estado general (verde/amarillo/rojo) - Resumen: motores estado, gensets estado, tanques niveles, alarmas activas - Roll/pitch actual (gauge visual) - Acceso rápido a sistemas más usados ### Lista de sistemas (menú lateral o pestaña) Generado dinámicamente desde el `.vmspack` activo. Solo muestra los sistemas que el integrador marcó en el wizard. ### Vista de mímico por sistema Carga el SVG del mímico empaquetado en el `.vmspack`. Se renderiza con `flutter_svg`. Los elementos del mímico se actualizan con WebSocket en tiempo real: - Cambios de color por estado - Valores numéricos sobre los símbolos - Tap en un elemento → muestra detalle del tag ### Panel de Trim y Maniobra (destacado) Esta es la pantalla estrella para el owner. UI optimizada para uso con una mano mientras conduce: - Sliders verticales grandes para cada actuador de trim (touch-friendly) - Indicador horizontal de roll en vivo (animado) - Indicador de pitch - Botón grande rojo de "RESET EMERGENCIA" - Toggle de "Modo Manual del Owner" (requiere biométrico para activar) - Indicación clara del envelope activo (línea visual donde el sistema empieza a bloquear) ### Panel de alarmas - Lista cronológica con color por prioridad - Notificaciones push locales cuando entra alarma crítica (incluso con la app en background) - Botón ACK por alarma (respeta permisos del rol) - Filtros y búsqueda ### Trends - Gráficas con fl_chart - Múltiples tags simultáneos - Zoom y pan - Selector de rango temporal (1h, 24h, 7d) ### Log Book - Lista de entradas cronológica - Filtros por categoría (eventos motor, alarmas, manuales) - Botón "Agregar entrada manual" (Técnico/Admin) ### Settings - Cambiar password - Cerrar sesión / desautorizar dispositivo - Configuración de notificaciones - Idioma - Modo oscuro --- ## 5. Permisos por rol en la app Mismos roles que en estación desktop, pero con restricciones adicionales por ser móvil: | Acción | Operador | Técnico | Admin (owner) | |---|---|---|---| | Ver mímicos | ✓ | ✓ | ✓ | | Ver alarmas | ✓ | ✓ | ✓ | | ACK alarmas Info/Low | ✓ | ✓ | ✓ | | ACK alarmas High/Emergency | ✗ | ✓ | ✓ | | Ver trends | ✓ | ✓ | ✓ | | Ver logbook | ✓ | ✓ | ✓ | | Agregar entrada manual logbook | ✗ | ✓ | ✓ | | Control de luces, A/A (no críticos) | ✗ | ✓ | ✓ | | Control trim | ✗ | ✓ | ✓ | | Activar modo manual del owner | ✗ | ✗ | ✓ | | Reset emergencia | ✓ | ✓ | ✓ | | Arrancar motores / thrusters | ✗ | ✗ | ✗ (solo estación fija) | | Override de permissives WARNING | ✗ | ✗ | ✓ | | Gestión de usuarios y dispositivos | ✗ | ✗ | ✓ | **Regla de oro**: acciones de propulsión y maquinaria principal (arranque/parada motores, thrusters, parada de emergencia general) **solo desde estación fija puente o máquinas**, nunca desde móvil. Por seguridad. --- ## 6. Notificaciones push locales Sin servicios externos (no Firebase, no APNs externos). La app mantiene WebSocket persistente con el Runtime. Cuando recibe evento crítico: - Si la app está en foreground → notificación dentro de la app - Si la app está en background → notificación local del sistema operativo (`flutter_local_notifications`) - Sonido distintivo por prioridad - Tocar la notificación → abre la app en el evento específico Limitación: si el usuario fuerza-cierra la app, no llegan notificaciones. Documentar al owner. --- ## 7. Modo offline degradado Si la conexión WebSocket cae: - Banner persistente "Sin conexión al VMS" - Sigue mostrando los últimos valores conocidos (con badge "desactualizado X minutos") - Permite ver histórico cached - No permite enviar comandos - Intenta reconectar automáticamente cada 10s --- ## 8. Empaquetado y distribución ### iOS - Build con Xcode - Distribución vía TestFlight para clientes específicos (no App Store público inicialmente) - Requiere cuenta Apple Developer ($99/año) — gasto del integrador (Álvaro) - Cada armador recibe invitación TestFlight con el binario ### Android - Build APK firmado - Distribución directa por enlace (no Google Play inicialmente) - O via Play Store con cuenta de desarrollador ($25 una vez) si se decide más adelante **Por qué no stores públicas inicialmente:** - Las stores requieren accesibilidad pública (cualquiera puede instalar) - Eso introduce riesgo de seguridad (cualquiera podría intentar usar la app contra cualquier IP) - Distribución privada (TestFlight + APK firmado) garantiza que solo clientes autorizados tienen acceso --- ## 9. Sprints relacionados con Mobile - **Sprint 11**: VMS-Sailor Mobile completo - Setup Flutter project - Enrollment flow + QR scan - Login + TOTP + biométrico - WebSocket client + REST client - Pantallas: Overview, Lista sistemas, Mímico simple - Panel alarmas con notificaciones locales - Panel Trim (caso destacado) - Permisos por rol - Build iOS + Android - **Sprint posterior**: refinamientos, trends avanzados, modo offline robusto, logbook completo --- ## 10. Consideraciones de UI/UX móvil ### Touch targets Mínimo 44×44 pt en iOS, 48×48 dp en Android. Los sliders de trim deben ser grandes para uso con guantes o manos mojadas. ### Lecturabilidad bajo sol - Modo de pantalla "outdoor" con contraste aumentado - Texto grande por defecto (configurable) - Evitar colores pastel para datos críticos ### Orientación - Tablets: soportar landscape y portrait - Smartphones: portrait principalmente; landscape para mímicos complejos y trends ### Accesibilidad - Soporte de screen readers (VoiceOver iOS, TalkBack Android) - Tamaño de fuente respetando configuración del sistema - Contraste WCAG AA mínimo --- ## 11. Privacidad y datos La app NO recolecta: - Datos de telemetría de uso - Analítica - Crash reports a servidores externos La app guarda LOCALMENTE en almacenamiento seguro del dispositivo: - Secreto TOTP (cifrado en Keychain/Keystore) - Token de sesión - Últimos valores cached (para modo offline) - Configuración del usuario Si el dispositivo se restablece o se borra la app → todo se pierde. El owner debe enrolar nuevamente. --- **Fin de Parte 5 de 6.** Próxima: Parte 6 — Plan de Sprints completo.