# not-spielo/galaxywatch-ppg-logger architecture

> Architecture diagram of the GitHub repository not-spielo/galaxywatch-ppg-logger, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-11-10). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/not-spielo/galaxywatch-ppg-logger
- Repository: https://github.com/not-spielo/galaxywatch-ppg-logger

## Overview

GalaxyWatch PPG Logger combines a Wear OS collector with a PC session server for synchronizing watch signals and OBS video. The wearable workflow is permission and health-service setup, user-controlled foreground collection, and local Room persistence; the PC workflow manages sessions, markers, uploads, and video recording. Wearable calls are shown from excerpts and indexed references. Server capabilities are README-documented, but its internal routes and data flow were not sampled, so those relationships are left unverified.

Main entry points: [`wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt`](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt) (Collection controls), [`wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgDao.kt`](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgDao.kt) (PPG data access), [`server/main.py`](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/server/main.py) (Session API), [`wearable/src/main/java/kaist/iclab/galaxyppglogger/data/HealthTrackerService.kt`](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/data/HealthTrackerService.kt) (Health connection)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_wearable["Watch collection"]
  node_activity["Watch activity<br/>[MainActivity.kt]"]
  node_watch_ui["Control screen<br/>[MainScreen.kt]"]
  node_viewmodel["Collection controls"]
  node_service_state["Service state<br/>[ServiceState.kt]"]
  node_abstract_vm["View model API"]
  node_health_service["Health connection"]
  node_connection_state["Connection state<br/>[ConnectionState.kt]"]
  node_collector_controller["Collector controller"]
  node_collector_service["Foreground service"]
  node_abstract_collector["Collector base"]
  node_ppg_collector["PPG collector<br/>[PPGCollector.kt]"]
  node_ongoing_activity["Recording notification"]
end

subgraph group_server["PC session server"]
  node_server_api["Session API<br/>[main.py]"]
  node_session_data["Session files<br/>[main.py]"]
  node_data_convert["Watch data converter"]
end

subgraph group_storage["Watch persistence"]
  node_ppg_dao["PPG data access<br/>[PpgDao.kt]"]
  node_base_dao["DAO base<br/>[BaseDao.kt]"]
  node_ppg_entity["PPG records<br/>[PpgEntity.kt]"]
  node_room_db[("Room database<br/>[RoomDB.kt]")]
end

node_watch_user(("Watch user"))
node_obs["OBS Studio"]
node_server_client(("API client"))

node_watch_user -->|"opens"| node_activity
node_activity -->|"renders"| node_watch_ui
node_watch_ui -->|"uses controls"| node_abstract_vm
node_viewmodel -->|"implements"| node_abstract_vm
node_viewmodel -->|"updates"| node_service_state
node_activity -->|"starts connection"| node_health_service
node_viewmodel -->|"observes state"| node_health_service
node_health_service -->|"sets state"| node_connection_state
node_viewmodel -->|"starts or stops"| node_collector_controller
node_collector_controller -->|"starts service"| node_collector_service
node_collector_service -->|"starts and stops"| node_abstract_collector
node_collector_service -->|"creates notification"| node_ongoing_activity
node_ppg_collector -->|"extends"| node_abstract_collector
node_ppg_collector -->|"reads sensor data"| node_health_service
node_ppg_collector -->|"inserts records"| node_ppg_dao
node_ppg_dao -->|"extends"| node_base_dao
node_ppg_dao -->|"stores"| node_ppg_entity
node_viewmodel -->|"deletes on flush"| node_ppg_dao
node_room_db -->|"provides"| node_ppg_dao
node_room_db -->|"registers"| node_ppg_entity
node_server_client -->|"calls endpoints"| node_server_api
node_server_api -->|"controls recording"| node_obs
node_server_api -->|"manages session files"| node_session_data
node_server_client -->|"uploads CSV"| node_server_api
node_data_convert -.-> node_session_data

click node_activity "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/MainActivity.kt"
click node_watch_ui "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/MainScreen.kt"
click node_viewmodel "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt"
click node_service_state "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/ServiceState.kt"
click node_abstract_vm "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/AbstractViewModel.kt"
click node_health_service "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/data/HealthTrackerService.kt"
click node_connection_state "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/data/ConnectionState.kt"
click node_collector_controller "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorController.kt"
click node_collector_service "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorService.kt"
click node_abstract_collector "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/AbstractCollector.kt"
click node_ppg_collector "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PPGCollector.kt"
click node_ongoing_activity "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/OngoingActivityHelper.kt"
click node_ppg_dao "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgDao.kt"
click node_base_dao "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/BaseDao.kt"
click node_ppg_entity "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgEntity.kt"
click node_room_db "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/data/RoomDB.kt"
click node_server_api "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/server/main.py"
click node_session_data "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/server/main.py"
click node_data_convert "https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/server/convert_watch_data.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_activity,node_watch_ui,node_viewmodel,node_service_state,node_abstract_vm,node_health_service,node_connection_state,node_collector_controller,node_collector_service,node_abstract_collector,node_ppg_collector,node_ongoing_activity,node_watch_user,node_server_client toneBlue
class node_server_api,node_session_data,node_data_convert toneAmber
class node_ppg_dao,node_base_dao,node_ppg_entity,node_room_db toneMint
class node_obs toneIndigo
```

## Components

### Watch collection

- [Watch activity](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/MainActivity.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/MainActivity.kt`
- [Control screen](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/MainScreen.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/MainScreen.kt`
- [Collection controls](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt`
- [Service state](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/ServiceState.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/ServiceState.kt`
- [View model API](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/AbstractViewModel.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/AbstractViewModel.kt`
- [Health connection](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/data/HealthTrackerService.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/data/HealthTrackerService.kt`
- [Connection state](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/data/ConnectionState.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/data/ConnectionState.kt`
- [Collector controller](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorController.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorController.kt`
- [Foreground service](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorService.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorService.kt`
- [Collector base](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/AbstractCollector.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/AbstractCollector.kt`
- [PPG collector](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PPGCollector.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PPGCollector.kt`
- [Recording notification](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/OngoingActivityHelper.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/OngoingActivityHelper.kt`

### PC session server

- [Session API](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/server/main.py): `server/main.py`
- [Session files](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/server/main.py): `server/main.py`
- [Watch data converter](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/server/convert_watch_data.py): `server/convert_watch_data.py`

### Watch persistence

- [PPG data access](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgDao.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgDao.kt`
- [DAO base](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/BaseDao.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/BaseDao.kt`
- [PPG records](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgEntity.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgEntity.kt`
- [Room database](https://github.com/not-spielo/galaxywatch-ppg-logger/blob/main/wearable/src/main/java/kaist/iclab/galaxyppglogger/data/RoomDB.kt): `wearable/src/main/java/kaist/iclab/galaxyppglogger/data/RoomDB.kt`

### Other components

- **Watch user**
- **OBS Studio**
- **API client**

## Connections

- Watch user → Watch activity: opens (evidence: `README.md`)
- Watch activity → Control screen: renders (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/MainActivity.kt`)
- Control screen → View model API: uses controls (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/MainScreen.kt`)
- Collection controls → View model API: implements (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt`)
- Collection controls → Service state: updates (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt`)
- Watch activity → Health connection: starts connection (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/MainActivity.kt`)
- Collection controls → Health connection: observes state (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt`)
- Health connection → Connection state: sets state (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/data/HealthTrackerService.kt`)
- Collection controls → Collector controller: starts or stops (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt`)
- Collector controller → Foreground service: starts service (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorController.kt`)
- Foreground service → Collector base: starts and stops (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorService.kt`)
- Foreground service → Recording notification: creates notification (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/CollectorService.kt`)
- PPG collector → Collector base: extends (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PPGCollector.kt`)
- PPG collector → Health connection: reads sensor data (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PPGCollector.kt`)
- PPG collector → PPG data access: inserts records (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PPGCollector.kt`)
- PPG data access → DAO base: extends (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgDao.kt`)
- PPG data access → PPG records: stores (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/collector/PPG/PpgDao.kt`)
- Collection controls → PPG data access: deletes on flush (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/ui/RealViewModelImpl.kt`)
- Room database → PPG data access: provides (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/data/RoomDB.kt`)
- Room database → PPG records: registers (evidence: `wearable/src/main/java/kaist/iclab/galaxyppglogger/data/RoomDB.kt`)
- API client → Session API: calls endpoints (evidence: `README.md`)
- Session API → OBS Studio: controls recording (evidence: `README.md`)
- Session API → Session files: manages session files (evidence: `README.md`)
- API client → Session API: uploads CSV (evidence: `README.md`)
- Watch data converter → Session files
