Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e766c52
add websocket connector backend and ipc protocol server
DennisMoschina Feb 24, 2026
da738f0
wire connectors settings page into app navigation and startup
DennisMoschina Feb 24, 2026
6dc4389
use wearableConnector for connections in websocket to share connected…
DennisMoschina Feb 25, 2026
fd9406c
feat: Implement command structure for wearable connector
DennisMoschina Feb 25, 2026
e22ff98
feat: Add AsyncScanCommand for asynchronous scanning and event streaming
DennisMoschina Feb 26, 2026
8eef9de
feat: Add WebSocket IPC API documentation for OpenWearable connector
DennisMoschina Feb 26, 2026
9cd7019
feat(connectors_page.dart): Update WebSocket IPC terminology and add …
DennisMoschina Mar 2, 2026
2beb530
feat: Implement device IP address resolution and integrate into conne…
DennisMoschina Mar 2, 2026
6a849d1
feat: Update iOS project settings and dependencies
DennisMoschina Mar 3, 2026
ed7e81b
feat: Update WebSocket IPC server to resolve device IP address and im…
DennisMoschina Mar 3, 2026
7bbc732
feat(connectors): migrate websocket audio playback to flutter_sound
DennisMoschina Mar 9, 2026
9cf503f
feat(connectors): add websocket audio commands with codec configuration
DennisMoschina Mar 9, 2026
baa6feb
docs(connectors): document websocket audio sources and codec options
DennisMoschina Mar 9, 2026
f86cfb9
feat(connectors): switch websocket audio playback to audioplayers
DennisMoschina Mar 10, 2026
5fadbfb
docs(connectors): remove websocket audio streaming API docs
DennisMoschina Mar 10, 2026
f4b0c81
refactor(connectors): remove websocket URL audio playback
DennisMoschina Mar 10, 2026
9a6278e
feat(settings): add keep app in foreground setting to disable automat…
DennisMoschina Mar 11, 2026
3ff5bf6
refactor: regenerate generated files after rebase
DennisMoschina Apr 21, 2026
7bec06f
fix(dependencies): remove flutter_sound plugin references from genera…
DennisMoschina Apr 22, 2026
e09b654
feat(connectors): show active connector indicator
DennisMoschina Apr 23, 2026
db83ef2
fix(connectors): center active connector indicator
DennisMoschina Apr 23, 2026
6fb1560
feat(connectors): compact active connector indicator
DennisMoschina Apr 23, 2026
569ad9b
refactor(connectors): unify connector branding
DennisMoschina Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions open_wearable/.metadata
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
# This file should be version controlled and should not be manually edited.

version:
revision: "3297454732841b1a5a25d9f35f1fd5d7a4479e12"
channel: "main"
revision: "f5a8537f90d143abd5bb2f658fa69c388da9677b"
channel: "stable"

project_type: app

# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 3297454732841b1a5a25d9f35f1fd5d7a4479e12
base_revision: 3297454732841b1a5a25d9f35f1fd5d7a4479e12
create_revision: f5a8537f90d143abd5bb2f658fa69c388da9677b
base_revision: f5a8537f90d143abd5bb2f658fa69c388da9677b
- platform: ios
create_revision: 3297454732841b1a5a25d9f35f1fd5d7a4479e12
base_revision: 3297454732841b1a5a25d9f35f1fd5d7a4479e12
create_revision: f5a8537f90d143abd5bb2f658fa69c388da9677b
base_revision: f5a8537f90d143abd5bb2f658fa69c388da9677b

# User provided section

Expand Down
251 changes: 251 additions & 0 deletions open_wearable/docs/connectors/websocket-ipc-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
# WebSocket IPC API

This document describes how to communicate with the OpenWearable WebSocket connector.

## Endpoint

Default endpoint:

- `ws://<device-ip>:8765/ws`

Notes:

- The app binds the websocket server on all IPv4 interfaces and advertises the current device IP for clients on the same network.
- Port and path are configurable in app settings.
- The API is JSON over WebSocket text frames.

## Message Envelopes

Request:

```json
{"id":1,"method":"ping","params":{}}
```

Success response:

```json
{"id":1,"result":{"ok":true}}
```

Error response:

```json
{
"id": 1,
"error": {
"message": "Unknown method: foo",
"type": "UnsupportedError",
"stack": "..."
}
}
```

## Server Events

On connect, the server sends:

```json
{
"event": "ready",
"methods": ["ping", "methods", "..."],
"endpoint": "ws://192.168.1.23:8765/ws"
}
```

`ready.endpoint` may be `null` when the app cannot determine a client-reachable
LAN IP address. The connector still runs in that case.

Other event messages:

- `scan`: broadcast when a device is discovered.
- `connecting`: broadcast when a connect attempt starts.
- `connected`: broadcast when a wearable is connected.
- `stream`: stream subscription data.
- `stream_error`: error for a stream subscription.
- `stream_done`: stream finished.

`stream` event format:

```json
{
"event": "stream",
"subscription_id": 1,
"stream": "sensor_values",
"device_id": "string",
"data": {}
}
```

## Top-Level Methods

| Method | Params | Result |
|---|---|---|
| `ping` | `{}` | `{"ok":true}` |
| `methods` | `{}` | `string[]` |
| `has_permissions` | `{}` | `bool` |
| `check_and_request_permissions` | `{}` | `bool` |
| `start_scan` | `{"check_and_request_permissions"?:bool}` | `{"started":true}` |
| `start_scan_async` | `{"check_and_request_permissions"?:bool}` | `{"started":true,"subscription_id":int,"stream":"scan","device_id":"scanner"}` |
| `get_discovered_devices` | `{}` | `DiscoveredDevice[]` |
| `connect` | `{"device_id":string,"connected_via_system"?:bool}` | `WearableSummary` |
| `connect_system_devices` | `{"ignored_device_ids"?:string[]}` | `WearableSummary[]` |
| `list_connected` | `{}` | `WearableSummary[]` |
| `disconnect` | `{"device_id":string}` | `{"disconnected":true}` |
| `store_sound` | `{"sound_id":string,"audio_base64":string,"codec"?:string,"sample_rate"?:int,"num_channels"?:int,"interleaved"?:bool,"buffer_size"?:int}` | `{"sound_id":string,"stored":true,"bytes":int,"config":object}` |
| `play_sound` | `{"sound_id":string,"volume"?:number,"codec"?:string,"sample_rate"?:int,"num_channels"?:int}` | `{"source":"sound_id","sound_id":string,"playing":true,"config":object}` |
| `subscribe` | `{"device_id":string,"stream":string,"args"?:object}` | `{"subscription_id":int,"stream":string,"device_id":string}` |
| `unsubscribe` | `{"subscription_id":int}` | `{"subscription_id":int,"cancelled":bool}` |
| `invoke_action` | `{"device_id":string,"action":string,"args"?:object}` | depends on action |

## Action Commands (`invoke_action`)

Current actions:

- `disconnect` (no `args`)
- `synchronize_time`
- `list_sensors`
- `list_sensor_configurations`
- `set_sensor_configuration` with args:
- `{"configuration_name":string,"value_key":string}`

Examples:

```json
{"id":10,"method":"invoke_action","params":{"device_id":"abc","action":"synchronize_time"}}
```

```json
{"id":11,"method":"invoke_action","params":{"device_id":"abc","action":"set_sensor_configuration","args":{"configuration_name":"Accelerometer","value_key":"100Hz"}}}
```

## Subscribe Streams

Supported values for `subscribe.params.stream`:

- `sensor_values` (requires one of below in `args`)
- `{"sensor_id":string}` (recommended)
- `{"sensor_index":int}`
- `{"sensor_name":string}`
- `sensor_configuration`
- `button_events`
- `battery_percentage`
- `battery_power_status`
- `battery_health_status`
- `battery_energy_status`

Note:

- `scan` is not a direct `subscribe` stream.
- Use `start_scan_async` to receive scan data via `stream` events.

## Audio Playback Over WebSocket

The connector supports distinct preloaded sounds (store once, play many times).

### 1) Distinct Preloaded Sounds

Store sound bytes in memory:

```json
{
"id": 20,
"method": "store_sound",
"params": {
"sound_id": "beep_ok",
"audio_base64": "<base64-encoded-audio-bytes>"
}
}
```

Play a stored sound:

```json
{
"id": 21,
"method": "play_sound",
"params": {
"sound_id": "beep_ok",
"volume": 1.0
}
}
```

`play_sound` requires `sound_id`.

## Data Shapes

### DiscoveredDevice

```json
{
"id": "string",
"name": "string",
"service_uuids": ["string"],
"manufacturer_data": [1, 2, 3],
"rssi": -56
}
```

### WearableSummary

```json
{
"device_id": "string",
"name": "string",
"type": "OpenEarableV2",
"capabilities": ["SensorManager", "SensorConfigurationManager"]
}
```

### `list_sensors` item

```json
{
"sensor_id": "accelerometer_0",
"sensor_index": 0,
"name": "Accelerometer",
"chart_title": "Accelerometer",
"short_chart_title": "ACC",
"axis_names": ["x", "y", "z"],
"axis_units": ["m/s²", "m/s²", "m/s²"],
"timestamp_exponent": -9
}
```

### `list_sensor_configurations` item

```json
{
"name": "Accelerometer",
"unit": "Hz",
"values": [
{
"key": "100Hz",
"frequency_hz": 100,
"options": ["streamSensorConfigOption"]
}
],
"off_value": "off"
}
```

## Suggested Workflows

### Scan and connect

1. Call `start_scan` or `start_scan_async`.
2. Use `get_discovered_devices` (or consume stream events from `start_scan_async`).
3. Call `connect` with selected `device_id`.

### Sensor streaming

1. `invoke_action` with `action="list_sensors"`.
2. Pick `sensor_id`.
3. `subscribe` with `stream="sensor_values"` and `args={"sensor_id":"..."}`.
4. `unsubscribe` when done.

### Distinct sound playback

1. `store_sound` with `sound_id` and `audio_base64`.
2. `play_sound` with the same `sound_id`.
2 changes: 2 additions & 0 deletions open_wearable/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>
2 changes: 0 additions & 2 deletions open_wearable/ios/Flutter/Profile.xcconfig

This file was deleted.

2 changes: 1 addition & 1 deletion open_wearable/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '13.0'
# platform :ios, '13.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
23 changes: 16 additions & 7 deletions open_wearable/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,23 @@ PODS:
- Flutter
- package_info_plus (0.4.5):
- Flutter
- package_info_plus (0.4.5):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
- SDWebImage (5.21.5):
- SDWebImage/Core (= 5.21.5)
- SDWebImage/Core (5.21.5)
- SDWebImage (5.21.6):
- SDWebImage/Core (= 5.21.6)
- SDWebImage/Core (5.21.6)
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- SwiftCBOR (0.4.7)
- SwiftProtobuf (1.33.3)
- SwiftProtobuf (1.34.1)
- SwiftyGif (5.4.5)
- universal_ble (0.0.1):
- Flutter
Expand All @@ -83,6 +88,7 @@ DEPENDENCIES:
- flutter_archive (from `.symlinks/plugins/flutter_archive/ios`)
- mcumgr_flutter (from `.symlinks/plugins/mcumgr_flutter/ios`)
- open_file_ios (from `.symlinks/plugins/open_file_ios/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
Expand Down Expand Up @@ -117,6 +123,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/mcumgr_flutter/ios"
open_file_ios:
:path: ".symlinks/plugins/open_file_ios/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
permission_handler_apple:
Expand All @@ -143,19 +151,20 @@ SPEC CHECKSUMS:
iOSMcuManagerLibrary: e9555825af11a61744fe369c12e1e66621061b58
mcumgr_flutter: 969e99cc15e9fe658242669ce1075bf4612aef8a
open_file_ios: 46184d802ee7959203f6392abcfa0dd49fdb5be0
path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838
SDWebImage: 1bb6a1b84b6fe87b972a102bdc77dd589df33477
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
SwiftCBOR: 465775bed0e8bac7bfb8160bcf7b95d7f75971e4
SwiftProtobuf: e1b437c8e31a4c5577b643249a0bb62ed4f02153
SwiftProtobuf: c901f00a3e125dc33cac9b16824da85682ee47da
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
universal_ble: ff19787898040d721109c6324472e5dd4bc86adc
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c

PODFILE CHECKSUM: 251cb053df7158f337c0712f2ab29f4e0fa474ce
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e

COCOAPODS: 1.16.2
Loading
Loading