@glassly/bluetooth-sdk 0.3.1-dev.537 → 0.3.1-dev.539
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +3 -3
- package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +36 -15
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +12 -5
- package/build/generated/releaseMetadata.js +3 -3
- package/build/generated/releaseMetadata.js.map +1 -1
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/GeneratedReleaseMetadata.swift +3 -3
- package/ios/Source/controllers/R1.swift +25 -9
- package/ios/Source/sgcs/G2.swift +12 -5
- package/package.json +1 -1
- package/src/generated/releaseMetadata.ts +3 -3
|
@@ -3,9 +3,9 @@ package com.glassly.bluetoothsdk
|
|
|
3
3
|
/** Generated by release CI. Do not edit in a release checkout. */
|
|
4
4
|
internal object GeneratedReleaseMetadata {
|
|
5
5
|
const val FAMILY_BASE_VERSION: String = "0.3.1"
|
|
6
|
-
const val RELEASE_IDENTITY: String = "0.3.1-dev.
|
|
7
|
-
const val RELEASE_SET_ID: String = "glassly-0.3.1-dev.
|
|
8
|
-
const val SOURCE_COMMIT: String = "
|
|
6
|
+
const val RELEASE_IDENTITY: String = "0.3.1-dev.539"
|
|
7
|
+
const val RELEASE_SET_ID: String = "glassly-0.3.1-dev.539"
|
|
8
|
+
const val SOURCE_COMMIT: String = "c2bddf404b5625b0facd5f1e9204f3a889a0ac80"
|
|
9
9
|
const val OTA_MANIFEST_URL: String = ""
|
|
10
10
|
const val OTA_MANIFEST_SHA256: String = ""
|
|
11
11
|
}
|
|
@@ -49,6 +49,31 @@ private object R1BLE {
|
|
|
49
49
|
/** `source` on emitted touch events; the same value the G2 CFW tags
|
|
50
50
|
* ring input it relays with, so JS treats both paths alike. */
|
|
51
51
|
const val RING_SOURCE = 2
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Scan-response manufacturer data (openCFW: `0x5245` company id, the six
|
|
55
|
+
* address bytes in SoftDevice order, then an optional 15-byte serial).
|
|
56
|
+
* Android hands us the payload with the company id already stripped. The
|
|
57
|
+
* GAP name is `EVEN R1_` + address bytes 3,2,1, which pins the byte order;
|
|
58
|
+
* the returned string keeps SoftDevice order, the form the glasses have
|
|
59
|
+
* accepted in RING_CONNECT_INFO. Taking the LAST six bytes was wrong for
|
|
60
|
+
* rings with a serial (it returned ASCII digits).
|
|
61
|
+
*/
|
|
62
|
+
fun macAddress(mfgBytes: ByteArray, ringId: String?): String? {
|
|
63
|
+
if (mfgBytes.size < 6) return null
|
|
64
|
+
val addr = mfgBytes.copyOfRange(0, 6)
|
|
65
|
+
val hex = { b: ByteArray -> b.joinToString("") { String.format("%02X", it) } }
|
|
66
|
+
val id = ringId?.uppercase()
|
|
67
|
+
val chosen = when {
|
|
68
|
+
id.isNullOrEmpty() -> addr
|
|
69
|
+
hex(byteArrayOf(addr[3], addr[2], addr[1])) == id -> addr
|
|
70
|
+
else -> {
|
|
71
|
+
val flipped = addr.reversedArray()
|
|
72
|
+
if (hex(byteArrayOf(flipped[3], flipped[2], flipped[1])) == id) flipped else return null
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return chosen.joinToString(":") { String.format("%02X", it) }
|
|
76
|
+
}
|
|
52
77
|
}
|
|
53
78
|
|
|
54
79
|
// MARK: - Ring → phone touch report (channel 1)
|
|
@@ -483,14 +508,14 @@ class R1 : ControllerManager() {
|
|
|
483
508
|
val mfgHex = mfgBytes?.joinToString(" ") { String.format("%02X", it) } ?: "none"
|
|
484
509
|
Bridge.log("R1: Discovered: ${deviceName ?: "?"} (RSSI: ${result.rssi}) mfgData: $mfgHex")
|
|
485
510
|
|
|
486
|
-
// Extract ring MAC from manufacturer data
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
511
|
+
// Extract the ring MAC from manufacturer data and store name->MAC map.
|
|
512
|
+
if (deviceName != null && mfgBytes != null) {
|
|
513
|
+
val macStr = R1BLE.macAddress(mfgBytes, extractRingId(deviceName))
|
|
514
|
+
if (macStr != null) {
|
|
515
|
+
putRingMacInMap(deviceName, macStr)
|
|
516
|
+
} else {
|
|
517
|
+
Bridge.log("R1: mfgData carries no address matching $deviceName")
|
|
518
|
+
}
|
|
494
519
|
}
|
|
495
520
|
|
|
496
521
|
// Emit discovered device
|
|
@@ -640,15 +665,11 @@ class R1 : ControllerManager() {
|
|
|
640
665
|
}
|
|
641
666
|
|
|
642
667
|
/**
|
|
643
|
-
* One emit path for both direct parsers
|
|
644
|
-
*
|
|
645
|
-
*
|
|
668
|
+
* One emit path for both direct parsers. The ring reports touch to a
|
|
669
|
+
* single peer, so whatever arrives here is the only copy — forward it
|
|
670
|
+
* whichever route is configured.
|
|
646
671
|
*/
|
|
647
672
|
private fun emitGesture(gestureName: String, timestamp: Long, via: String) {
|
|
648
|
-
if (routeViaGlasses) {
|
|
649
|
-
Bridge.log("R1: $via $gestureName ignored (ring routed via glasses)")
|
|
650
|
-
return
|
|
651
|
-
}
|
|
652
673
|
Bridge.log("R1: $via: $gestureName")
|
|
653
674
|
Bridge.sendTouchEvent(ControllerTypes.R1, gestureName, timestamp, R1BLE.RING_SOURCE)
|
|
654
675
|
}
|
|
@@ -5605,6 +5605,9 @@ class G2 : SGCManager() {
|
|
|
5605
5605
|
private val ringViaGlasses: Boolean
|
|
5606
5606
|
get() = DeviceStore.get("bluetooth", "ring_via_glasses") as? Boolean ?: false
|
|
5607
5607
|
|
|
5608
|
+
private var lastRingReleaseAt = 0L
|
|
5609
|
+
private val RING_RELEASE_INTERVAL_MS = 5_000L
|
|
5610
|
+
|
|
5608
5611
|
/** RING_CONNECT_INFO for the paired ring. False when the glasses aren't
|
|
5609
5612
|
* booted or no ring MAC is known. */
|
|
5610
5613
|
private fun sendRingConnectInfo(connect: Boolean, tag: String): Boolean {
|
|
@@ -6788,11 +6791,15 @@ class G2 : SGCManager() {
|
|
|
6788
6791
|
// source unspecified, so the id alone also marks the ring.
|
|
6789
6792
|
val fromRing = eventSource == 2 || eventType == OsEventType.RING_TOUCH_DOWN
|
|
6790
6793
|
if (fromRing && !ringViaGlasses) {
|
|
6791
|
-
// The
|
|
6792
|
-
//
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6794
|
+
// The ring only reports touch to one peer, so this input is
|
|
6795
|
+
// real and must flow — but the glasses shouldn't hold the ring
|
|
6796
|
+
// while it is phone-routed. Ask them to let go (not per event).
|
|
6797
|
+
val now = System.currentTimeMillis()
|
|
6798
|
+
if (now - lastRingReleaseAt > RING_RELEASE_INTERVAL_MS) {
|
|
6799
|
+
lastRingReleaseAt = now
|
|
6800
|
+
Bridge.log("G2: ring $gestureName via glasses while phone-routed — releasing ring")
|
|
6801
|
+
releaseController()
|
|
6802
|
+
}
|
|
6796
6803
|
}
|
|
6797
6804
|
|
|
6798
6805
|
Bridge.sendTouchEvent(DeviceTypes.G2, gestureName, timestamp, eventSource)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export const BLUETOOTH_SDK_RELEASE_METADATA = Object.freeze({
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"familyBaseVersion": "0.3.1",
|
|
4
|
-
"releaseIdentity": "0.3.1-dev.
|
|
5
|
-
"releaseSetId": "glassly-0.3.1-dev.
|
|
6
|
-
"sourceCommit": "
|
|
4
|
+
"releaseIdentity": "0.3.1-dev.539",
|
|
5
|
+
"releaseSetId": "glassly-0.3.1-dev.539",
|
|
6
|
+
"sourceCommit": "c2bddf404b5625b0facd5f1e9204f3a889a0ac80",
|
|
7
7
|
"otaManifestUrl": null,
|
|
8
8
|
"otaManifestSha256": null
|
|
9
9
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,8BAA8B,GAA0C,MAAM,CAAC,MAAM,CAAC;IACjG,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;CAC1B,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface BluetoothSdkReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"0.3.1\",\n \"releaseIdentity\": \"0.3.1-dev.
|
|
1
|
+
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,8BAA8B,GAA0C,MAAM,CAAC,MAAM,CAAC;IACjG,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,uBAAuB;IACvC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;CAC1B,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface BluetoothSdkReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"0.3.1\",\n \"releaseIdentity\": \"0.3.1-dev.539\",\n \"releaseSetId\": \"glassly-0.3.1-dev.539\",\n \"sourceCommit\": \"c2bddf404b5625b0facd5f1e9204f3a889a0ac80\",\n \"otaManifestUrl\": null,\n \"otaManifestSha256\": null\n})\n"]}
|
|
@@ -11,7 +11,7 @@ enum BluetoothSdkDefaults {
|
|
|
11
11
|
static let voiceActivityDetectionEnabled = false
|
|
12
12
|
static let loudnessGateEnabled = true
|
|
13
13
|
private static let infoSdkVersionKey = "BluetoothSdkVersion"
|
|
14
|
-
private static let swiftPackageSdkVersion = "0.3.1-dev.
|
|
14
|
+
private static let swiftPackageSdkVersion = "0.3.1-dev.539"
|
|
15
15
|
private static let swiftPackageSdkVersionPlaceholder = "__GLASSLY" + "_BLUETOOTH_SDK_VERSION__"
|
|
16
16
|
|
|
17
17
|
private static func normalizedSdkVersion(_ value: String?) -> String? {
|
|
@@ -3,9 +3,9 @@ import Foundation
|
|
|
3
3
|
/// Generated by release CI. Do not edit in a release checkout.
|
|
4
4
|
enum GeneratedReleaseMetadata {
|
|
5
5
|
static let familyBaseVersion = "0.3.1"
|
|
6
|
-
static let releaseIdentity = "0.3.1-dev.
|
|
7
|
-
static let releaseSetId = "glassly-0.3.1-dev.
|
|
8
|
-
static let sourceCommit = "
|
|
6
|
+
static let releaseIdentity = "0.3.1-dev.539"
|
|
7
|
+
static let releaseSetId = "glassly-0.3.1-dev.539"
|
|
8
|
+
static let sourceCommit = "c2bddf404b5625b0facd5f1e9204f3a889a0ac80"
|
|
9
9
|
static let otaManifestUrl = ""
|
|
10
10
|
static let otaManifestSha256 = ""
|
|
11
11
|
}
|
|
@@ -12,6 +12,24 @@ import Foundation
|
|
|
12
12
|
// MARK: - R1 BLE Constants
|
|
13
13
|
|
|
14
14
|
private enum R1BLE {
|
|
15
|
+
/// Scan-response manufacturer data (openCFW: `0x5245` company id, the six
|
|
16
|
+
/// address bytes in SoftDevice order, then an optional 15-byte serial).
|
|
17
|
+
/// The GAP name is `EVEN R1_` + address bytes 3,2,1, which pins the byte
|
|
18
|
+
/// order; the stored string keeps SoftDevice order, the form the glasses
|
|
19
|
+
/// have accepted in RING_CONNECT_INFO. Taking the LAST six bytes was wrong
|
|
20
|
+
/// for rings with a serial (it returned ASCII digits).
|
|
21
|
+
static func macAddress(mfgData: Data, ringId: String?) -> Data? {
|
|
22
|
+
let bytes = [UInt8](mfgData)
|
|
23
|
+
guard bytes.count >= 8, bytes[0] == 0x45, bytes[1] == 0x52 else { return nil }
|
|
24
|
+
let addr = Array(bytes[2 ..< 8])
|
|
25
|
+
guard let ringId, !ringId.isEmpty else { return Data(addr) }
|
|
26
|
+
let hex = { (b: [UInt8]) in b.map { String(format: "%02X", $0) }.joined() }
|
|
27
|
+
if hex([addr[3], addr[2], addr[1]]) == ringId.uppercased() { return Data(addr) }
|
|
28
|
+
let flipped = Array(addr.reversed())
|
|
29
|
+
if hex([flipped[3], flipped[2], flipped[1]]) == ringId.uppercased() { return Data(flipped) }
|
|
30
|
+
return nil
|
|
31
|
+
}
|
|
32
|
+
|
|
15
33
|
/// Ring custom service
|
|
16
34
|
static let SERVICE_UUID = CBUUID(string: "BAE80001-4F05-4503-8E65-3AF1F7329D1F")
|
|
17
35
|
|
|
@@ -491,14 +509,10 @@ class R1: NSObject, ControllerManager {
|
|
|
491
509
|
applyRoute(viaGlasses: viaGlasses)
|
|
492
510
|
}
|
|
493
511
|
|
|
494
|
-
/// One emit path for both direct parsers
|
|
495
|
-
///
|
|
496
|
-
///
|
|
512
|
+
/// One emit path for both direct parsers. The ring reports touch to a
|
|
513
|
+
/// single peer, so whatever arrives here is the only copy — forward it
|
|
514
|
+
/// whichever route is configured.
|
|
497
515
|
private func emitGesture(_ gestureName: String, timestamp: Int64, via: String) {
|
|
498
|
-
guard !routeViaGlasses else {
|
|
499
|
-
Bridge.log("R1: \(via) \(gestureName) ignored (ring routed via glasses)")
|
|
500
|
-
return
|
|
501
|
-
}
|
|
502
516
|
Bridge.log("R1: \(via): \(gestureName)")
|
|
503
517
|
Bridge.sendTouchEvent(
|
|
504
518
|
deviceModel: ControllerTypes.R1,
|
|
@@ -761,8 +775,10 @@ extension R1: CBCentralManagerDelegate {
|
|
|
761
775
|
Bridge.log(
|
|
762
776
|
"R1: mfgData: \(mfgData.map { String(format: "%02X", $0) }.joined(separator: " "))"
|
|
763
777
|
)
|
|
764
|
-
if mfgData
|
|
765
|
-
self.ringMacAddressMap[name ?? ""] =
|
|
778
|
+
if let mac = R1BLE.macAddress(mfgData: mfgData, ringId: name.flatMap(self.extractRingId)) {
|
|
779
|
+
self.ringMacAddressMap[name ?? ""] = mac
|
|
780
|
+
} else {
|
|
781
|
+
Bridge.log("R1: mfgData carries no address matching \(name ?? "?")")
|
|
766
782
|
}
|
|
767
783
|
}
|
|
768
784
|
|
package/ios/Source/sgcs/G2.swift
CHANGED
|
@@ -5820,6 +5820,9 @@ final class G2: NSObject, SGCManager {
|
|
|
5820
5820
|
DeviceStore.shared.get("bluetooth", "ring_via_glasses") as? Bool ?? false
|
|
5821
5821
|
}
|
|
5822
5822
|
|
|
5823
|
+
private var lastRingReleaseAt: TimeInterval = 0
|
|
5824
|
+
private let RING_RELEASE_INTERVAL_S: TimeInterval = 5
|
|
5825
|
+
|
|
5823
5826
|
/// RING_CONNECT_INFO for the paired ring. False when the glasses aren't
|
|
5824
5827
|
/// booted or no ring MAC is known.
|
|
5825
5828
|
@discardableResult
|
|
@@ -7095,11 +7098,15 @@ final class G2: NSObject, SGCManager {
|
|
|
7095
7098
|
// source unspecified, so the id alone also marks the ring.
|
|
7096
7099
|
let fromRing = eventSource == 2 || eventType == .ringTouchDown
|
|
7097
7100
|
if fromRing, !ringViaGlasses {
|
|
7098
|
-
// The
|
|
7099
|
-
//
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7101
|
+
// The ring only reports touch to one peer, so this input is
|
|
7102
|
+
// real and must flow — but the glasses shouldn't hold the ring
|
|
7103
|
+
// while it is phone-routed. Ask them to let go (not per event).
|
|
7104
|
+
let now = Date().timeIntervalSince1970
|
|
7105
|
+
if now - lastRingReleaseAt > RING_RELEASE_INTERVAL_S {
|
|
7106
|
+
lastRingReleaseAt = now
|
|
7107
|
+
Bridge.log("G2: ring \(gestureName) via glasses while phone-routed — releasing ring")
|
|
7108
|
+
releaseController()
|
|
7109
|
+
}
|
|
7103
7110
|
}
|
|
7104
7111
|
|
|
7105
7112
|
Bridge.sendTouchEvent(
|
package/package.json
CHANGED
|
@@ -12,9 +12,9 @@ export interface BluetoothSdkReleaseMetadata {
|
|
|
12
12
|
export const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({
|
|
13
13
|
"schemaVersion": 1,
|
|
14
14
|
"familyBaseVersion": "0.3.1",
|
|
15
|
-
"releaseIdentity": "0.3.1-dev.
|
|
16
|
-
"releaseSetId": "glassly-0.3.1-dev.
|
|
17
|
-
"sourceCommit": "
|
|
15
|
+
"releaseIdentity": "0.3.1-dev.539",
|
|
16
|
+
"releaseSetId": "glassly-0.3.1-dev.539",
|
|
17
|
+
"sourceCommit": "c2bddf404b5625b0facd5f1e9204f3a889a0ac80",
|
|
18
18
|
"otaManifestUrl": null,
|
|
19
19
|
"otaManifestSha256": null
|
|
20
20
|
})
|