# Phase 1: Flutter Keyboard Core Implementation Plan **Status:** In Progress **Duration:** Week 3-5 **Goal:** Build the core Flutter keyboard app with direct command execution and state tracking --- ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────────────┐ │ Flutter Keyboard App │ ├─────────────────────────────────────────────────────────────────┤ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ UI Layer │ │ BLoC Layer │ │ Data Layer │ │ │ │ (Screens) │◄─┤ (State) │◄─┤ (Services) │ │ │ └─────────────┘ └─────────────┘ └──────┬──────┘ │ │ │ │ │ ┌────────────────────────────────────────┼─────────────────┐ │ │ │ Service Layer │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌───┴───────┐ │ │ │ │ │BridgeService│ │ StateService│ │AlarmService│ │ │ │ │ │ (HTTP+WS) │ │ (Monitor+ │ │(Query+Track)│ │ │ │ │ └──────┬──────┘ │ Alarm) │ └─────┬─────┘ │ │ │ └─────────┼─────────┴─────────────┴────────┼──────────────┘ │ └────────────┼────────────────────────────────┼──────────────────┘ │ │ ▼ ▼ ┌────────────────┐ ┌────────────────┐ │ GeViScope/GCore│ │ GeViServer │ │ Bridges │ │ Bridge │ │ (7720/7721) │ │ (7710) │ └────────────────┘ └────────────────┘ ``` --- ## Phase 1 Tasks ### Task 1.1: Project Setup ✅ COMPLETED **Priority:** HIGH Create new Flutter project with proper structure. **Sub-tasks:** - [x] Create Flutter project: `copilot_keyboard` - [x] Set up directory structure (clean architecture) - [x] Add dependencies (BLoC, dio, web_socket_channel, etc.) - [x] Configure for Windows desktop target - [x] Create base config loading from `servers.json` **Directory Structure:** ``` copilot_keyboard/ ├── lib/ │ ├── main.dart │ ├── app.dart │ ├── config/ │ │ ├── app_config.dart │ │ └── server_config.dart │ ├── core/ │ │ ├── constants/ │ │ ├── errors/ │ │ └── utils/ │ ├── data/ │ │ ├── models/ │ │ ├── repositories/ │ │ └── services/ │ ├── domain/ │ │ ├── entities/ │ │ ├── repositories/ │ │ └── usecases/ │ └── presentation/ │ ├── blocs/ │ ├── screens/ │ └── widgets/ ├── assets/ │ └── config/ ├── test/ └── pubspec.yaml ``` --- ### Task 1.2: Bridge Service ✅ COMPLETED **Priority:** HIGH Create service to communicate with all bridges. **Sub-tasks:** - [ ] Create `BridgeService` class - [ ] Implement HTTP client for REST calls - [ ] Implement WebSocket client for event streaming - [ ] Add connection management (connect/disconnect/reconnect) - [ ] Route commands to correct bridge based on camera/monitor ID **Key Methods:** ```dart class BridgeService { // Connection Future connect(ServerConfig server); Future disconnect(String serverId); // Commands (routed to correct bridge) Future viewerConnectLive(int viewer, int channel); Future viewerClear(int viewer); Future ptzPan(int camera, String direction, int speed); Future ptzTilt(int camera, String direction, int speed); Future ptzZoom(int camera, String direction, int speed); Future ptzStop(int camera); Future ptzPreset(int camera, int preset); // Event stream Stream get eventStream; } ``` --- ### Task 1.3: State Service ✅ COMPLETED **Priority:** HIGH Track monitor and alarm state from events. **Sub-tasks:** - [ ] Create `StateService` class - [ ] Subscribe to bridge WebSocket events - [ ] Track monitor states (viewer → camera mapping) - [ ] Track alarm states (active alarms) - [ ] Provide state streams for UI **Key Methods:** ```dart class StateService { // Monitor state Stream> get monitorStates; MonitorState? getMonitorState(int viewerId); // Alarm state Stream> get activeAlarms; bool isMonitorBlocked(int viewerId); // Sync Future syncFromBridges(); } ``` --- ### Task 1.4: Alarm Service (GeViServer Query) ✅ COMPLETED **Priority:** HIGH Query initial alarm state from GeViServer on startup. **Sub-tasks:** - [ ] Create `AlarmService` class - [ ] Implement GeViServer bridge connection - [ ] Query active alarms on startup using GetFirstAlarm/GetNextAlarm pattern - [ ] Merge with event-based alarm tracking - [ ] Periodic sync (every 30 seconds) **Key Methods:** ```dart class AlarmService { Future> queryAllAlarms(); Future startPeriodicSync(Duration interval); void stopPeriodicSync(); } ``` --- ### Task 1.5: Keyboard Layout UI ✅ COMPLETED **Priority:** HIGH Build the main keyboard interface. **Sub-tasks:** - [ ] Create main keyboard screen layout - [ ] Camera selection grid (numbered buttons) - [ ] Monitor selection grid (numbered buttons) - [ ] PTZ control panel (joystick or directional buttons) - [ ] Preset buttons - [ ] Status display (current camera on selected monitor) - [ ] Alarm indicator panel **UI Components:** ``` ┌─────────────────────────────────────────────────────────────┐ │ COPILOT Keyboard [Status: Online] │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ │ │ CAMERAS │ │ MONITORS │ │ │ │ [1] [2] [3] [4] [5] │ │ [1] [2] [3] [4] │ │ │ │ [6] [7] [8] [9] [10] │ │ [5] [6] [7] [8] │ │ │ │ ... │ │ [9!][10][11][12] │ │ │ └─────────────────────────┘ └─────────────────────────┘ │ │ │ │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ │ │ PTZ CONTROL │ │ PRESETS │ │ │ │ [▲] │ │ [1] [2] [3] [4] │ │ │ │ [◄][●][►] │ │ [5] [6] [7] [8] │ │ │ │ [▼] │ │ │ │ │ │ [Z-] [Z+] │ │ │ │ │ └─────────────────────────┘ └─────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ ACTIVE ALARMS │ │ │ │ [!] Camera 5 - Motion Detected (10:30:15) │ │ │ │ [!] Camera 12 - Door Contact (10:28:42) │ │ │ └─────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` --- ### Task 1.6: BLoC Implementation ✅ COMPLETED **Priority:** HIGH Implement state management with BLoC pattern. **BLoCs to Create:** - [x] `ConnectionBloc` - Bridge connection state - [x] `CameraBloc` - Camera selection and routing - [x] `MonitorBloc` - Monitor state and selection - [x] `PtzBloc` - PTZ control state - [x] `AlarmBloc` - Alarm state and display --- ### Task 1.7: Server Routing Logic ✅ COMPLETED **Priority:** HIGH Route commands to correct bridge based on camera/monitor ranges. **Sub-tasks:** - [ ] Load server config from `servers.json` - [ ] Implement camera-to-server mapping - [ ] Implement monitor-to-server mapping - [ ] Handle cross-server scenarios (camera on server A → monitor on server B) **Routing Rules:** ```dart class ServerRouter { // Find which server owns a camera ServerConfig? getServerForCamera(int cameraId); // Find which server owns a monitor ServerConfig? getServerForMonitor(int monitorId); // Get bridge URL for a server String getBridgeUrl(String serverId); } ``` --- ### Task 1.8: Error Handling ✅ COMPLETED **Priority:** MEDIUM Implement basic error handling and recovery. **Sub-tasks:** - [ ] Connection error handling with retry - [ ] Command timeout handling - [ ] Offline/degraded mode detection - [ ] User-friendly error messages - [ ] Logging for debugging --- ## Dependencies (pubspec.yaml) ```yaml dependencies: flutter: sdk: flutter # State Management flutter_bloc: ^8.1.6 equatable: ^2.0.5 # Networking dio: ^5.7.0 web_socket_channel: ^3.0.1 # Local Storage shared_preferences: ^2.3.3 # Routing go_router: ^14.6.2 # Dependency Injection get_it: ^8.0.2 # Utilities json_annotation: ^4.9.0 rxdart: ^0.28.0 dev_dependencies: flutter_test: sdk: flutter build_runner: ^2.4.13 json_serializable: ^6.8.0 bloc_test: ^9.1.7 mocktail: ^1.0.4 ``` --- ## Success Criteria - [ ] App connects to all configured bridges on startup - [ ] Camera button switches camera to selected monitor - [ ] PTZ controls move the selected camera - [ ] Monitor states update in real-time from WebSocket events - [ ] Alarm states query from GeViServer on startup - [ ] Alarms update in real-time from events - [ ] Monitors with active alarms show visual indicator - [ ] App works in degraded mode if bridges unavailable --- ## Files to Create ``` copilot_keyboard/ ├── lib/ │ ├── main.dart │ ├── app.dart │ ├── injection_container.dart │ ├── config/ │ │ ├── app_config.dart │ │ └── server_config.dart │ ├── core/ │ │ ├── constants/ │ │ │ └── api_constants.dart │ │ ├── errors/ │ │ │ └── failures.dart │ │ └── utils/ │ │ └── logger.dart │ ├── data/ │ │ ├── models/ │ │ │ ├── monitor_state_model.dart │ │ │ ├── alarm_state_model.dart │ │ │ └── bridge_event_model.dart │ │ └── services/ │ │ ├── bridge_service.dart │ │ ├── state_service.dart │ │ └── alarm_service.dart │ ├── domain/ │ │ └── entities/ │ │ ├── monitor_state.dart │ │ ├── alarm_state.dart │ │ └── server_config.dart │ └── presentation/ │ ├── blocs/ │ │ ├── connection/ │ │ ├── camera/ │ │ ├── monitor/ │ │ ├── ptz/ │ │ └── alarm/ │ ├── screens/ │ │ └── keyboard_screen.dart │ └── widgets/ │ ├── camera_grid.dart │ ├── monitor_grid.dart │ ├── ptz_control.dart │ ├── preset_buttons.dart │ └── alarm_panel.dart └── pubspec.yaml ``` --- ## Next Phase Dependencies Phase 1 creates the foundation for: - **Phase 2:** Coordination layer (PRIMARY election, PTZ locks) - **Phase 3:** Advanced features (sequences, CrossSwitch rules)