Skip to content

Commit

Permalink
PR feedback and other improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
yatharthranjan committed Sep 9, 2024
1 parent 47013d4 commit 6c9c0e6
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 83 deletions.
17 changes: 17 additions & 0 deletions plugins/radar-android-polar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,20 @@ Add the provider `.polar.PolarProvider` to the Firebase Remote Config `plugins`
This plugin was build using the [POLAR BLE SDK][1].

[1]: https://github.com/polarofficial/polar-ble-sdk


## Information

On the **Polar Vantage V3**, streaming data is only possible when a training session is started on the watch (see https://github.com/polarofficial/polar-ble-sdk/issues/456).

Here are the instructions how to start data streaming:

1. Have a phone running the app with Polar SDK paired with the watch.
2. The sensor data sharing must be enabled for the phone in the watch pairing menu (General settings/Pair and sync/Paired devices/SDK/Share).
3. Go to the 'Start training' -menu in watch and choose the desired sport profile.
4. On phone running the app with Polar SDK connect phone with watch.
5. Start the recording in the app.
6. On the watch start the training recording or stay in 'Start training' -menu.

Note! When streaming data we would recommend removing Polar Flow app from the mobile if it was installed. All other phones or Polar devices not used for streaming should be shut down or at least Bluetooth turned off.
Also after each streaming session you might need to go back to the watch mode before starting a new exercise & streaming session.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class PolarManager(
private var accDisposable: Disposable? = null
private var ppiDisposable: Disposable? = null
private var ppgDisposable: Disposable? = null

init {
status = SourceStatusListener.Status.DISCONNECTED // red icon
name = service.getString(R.string.polarDisplayName)
Expand All @@ -64,9 +64,7 @@ class PolarManager(
override fun start(acceptableIds: Set<String>) {

status = SourceStatusListener.Status.READY // blue loading
logger.debug("Polar Device is $deviceId")

disconnectToPolarSDK(deviceId)
connectToPolarSDK()

register()
Expand Down Expand Up @@ -125,8 +123,7 @@ class PolarManager(
override fun deviceDisconnected(polarDeviceInfo: PolarDeviceInfo) {
logger.debug("Device disconnected ${polarDeviceInfo.deviceId}")
isDeviceConnected = false
status = SourceStatusListener.Status.DISCONNECTED // red circle

disconnect()
}

override fun bleSdkFeatureReady(
Expand All @@ -137,11 +134,10 @@ class PolarManager(
if (isDeviceConnected) {
logger.debug("Feature ready {} for {}", feature, deviceId)

if (feature == PolarBleApi.PolarBleSdkFeature.FEATURE_POLAR_DEVICE_TIME_SETUP) {
setDeviceTime(deviceId)
}

when (feature) {
PolarBleApi.PolarBleSdkFeature.FEATURE_POLAR_DEVICE_TIME_SETUP ->
setDeviceTime(deviceId)

PolarBleApi.PolarBleSdkFeature.FEATURE_POLAR_ONLINE_STREAMING -> {
streamHR()
streamEcg()
Expand All @@ -167,14 +163,17 @@ class PolarManager(
val batteryLevel = level.toFloat() / 100.0f
state.batteryLevel = batteryLevel
logger.debug("Battery level $level%, which is $batteryLevel at $currentTime")
send(
batteryLevelTopic,
PolarBatteryLevel(name, currentTime, currentTime, batteryLevel)
)
mHandler.execute {
send(
batteryLevelTopic,
PolarBatteryLevel(name, currentTime, currentTime, batteryLevel)
)
}
}

})

api.setApiLogger { s: String -> logger.debug("POLAR_API: {}", s) }

try {
deviceId = service.getPolarDevice()
if (deviceId == null) {
Expand All @@ -187,6 +186,33 @@ class PolarManager(
} catch (a: PolarInvalidArgument) {
a.printStackTrace()
}
}

override fun onClose() {
super.onClose()
if (autoConnectDisposable != null && !autoConnectDisposable!!.isDisposed) {
autoConnectDisposable?.dispose()
}
if (hrDisposable != null && !hrDisposable!!.isDisposed) {
hrDisposable?.dispose()
}
if (ecgDisposable != null && !ecgDisposable!!.isDisposed) {
ecgDisposable?.dispose()
}
if (accDisposable != null && !accDisposable!!.isDisposed) {
accDisposable?.dispose()
}
if (ppiDisposable != null && !ppiDisposable!!.isDisposed) {
ppiDisposable?.dispose()
}
if (ppgDisposable != null && !ppgDisposable!!.isDisposed) {
ppgDisposable?.dispose()
}

disconnectToPolarSDK(deviceId)
mHandler.stop {
wakeLock?.release()
}

}

Expand All @@ -200,7 +226,6 @@ class PolarManager(
}

autoConnectDisposable = Flowable.interval(0, 5, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
if (deviceId == null || !isDeviceConnected) {
searchPolarDevice(true)
Expand All @@ -211,7 +236,6 @@ class PolarManager(
private fun searchPolarDevice(force: Boolean = false): Disposable? {
try {
return api.searchForDevice()
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ device: PolarDeviceInfo ->
logger.debug("Device found: ${device.deviceId} ${device.name}")
if (deviceId == null || force) {
Expand Down Expand Up @@ -267,7 +291,6 @@ class PolarManager(
if (isDisposed) {
hrDisposable = deviceId?.let {
api.startHrStreaming(it)
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { logger.debug("Subscribed to HrStreaming for $deviceId") }
.subscribe(
{ hrData: PolarHrData ->
Expand All @@ -279,19 +302,21 @@ class PolarManager(
"contactStatus: ${sample.contactStatus} " +
"contactStatusSupported: ${sample.contactStatusSupported}"
)
send(
heartRateTopic,
PolarHeartRate(
name,
getTimeNano(),
currentTime,
sample.hr,
sample.rrsMs,
sample.rrAvailable,
sample.contactStatus,
sample.contactStatusSupported
mHandler.execute {
send(
heartRateTopic,
PolarHeartRate(
name,
getTimeNano(),
currentTime,
sample.hr,
sample.rrsMs,
sample.rrAvailable,
sample.contactStatus,
sample.contactStatusSupported
)
)
)
}

}
},
Expand Down Expand Up @@ -328,15 +353,17 @@ class PolarManager(
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp)
} currentTime: $currentTime PolarTimeStamp: ${data.timeStamp}"
)
send(
ecgTopic,
PolarEcg(
name,
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp),
currentTime,
data.voltage
mHandler.execute {
send(
ecgTopic,
PolarEcg(
name,
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp),
currentTime,
data.voltage
)
)
)
}
}
},
{ error: Throwable ->
Expand Down Expand Up @@ -369,17 +396,19 @@ class PolarManager(
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp)
} currentTime: $currentTime PolarTimeStamp: ${data.timeStamp}"
)
send(
accelerationTopic,
PolarAcceleration(
name,
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp),
currentTime,
data.x,
data.y,
data.z
mHandler.execute {
send(
accelerationTopic,
PolarAcceleration(
name,
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp),
currentTime,
data.x,
data.y,
data.z
)
)
)
}
}
},
{ error: Throwable ->
Expand Down Expand Up @@ -416,18 +445,20 @@ class PolarManager(
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp)
} currentTime: $currentTime PolarTimeStamp: ${data.timeStamp}"
)
send(
ppgTopic,
PolarPpg(
name,
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp),
currentTime,
data.channelSamples[0],
data.channelSamples[1],
data.channelSamples[2],
data.channelSamples[3]
mHandler.execute {
send(
ppgTopic,
PolarPpg(
name,
PolarUtils.convertEpochPolarToUnixEpoch(data.timeStamp),
currentTime,
data.channelSamples[0],
data.channelSamples[1],
data.channelSamples[2],
data.channelSamples[3]
)
)
)
}
}
}
},
Expand All @@ -447,7 +478,6 @@ class PolarManager(
if (isDisposed) {
ppiDisposable = deviceId?.let {
api.startPpiStreaming(it)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ ppiData: PolarPpiData ->
for (sample in ppiData.samples) {
Expand All @@ -456,20 +486,22 @@ class PolarManager(
"errorEstimate: ${sample.errorEstimate} " +
"currentTime: $currentTime"
)
send(
ppIntervalTopic,
PolarPpInterval(
name,
currentTime,
currentTime,
sample.blockerBit,
sample.errorEstimate,
sample.hr,
sample.ppi,
sample.skinContactStatus,
sample.skinContactSupported
mHandler.execute {
send(
ppIntervalTopic,
PolarPpInterval(
name,
currentTime,
currentTime,
sample.blockerBit,
sample.errorEstimate,
sample.hr,
sample.ppi,
sample.skinContactStatus,
sample.skinContactSupported
)
)
)
}
}
},
{ error: Throwable ->
Expand All @@ -482,7 +514,7 @@ class PolarManager(
ppiDisposable?.dispose()
}
}

companion object {
private val logger = LoggerFactory.getLogger(PolarManager::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
package org.radarbase.passive.polar

import android.content.Context
import android.os.Process
import android.content.SharedPreferences
import org.radarbase.android.config.SingleRadarConfiguration
import org.radarbase.android.source.SourceManager
import org.radarbase.android.source.SourceService
import org.radarbase.android.util.SafeHandler

/**
* A service that manages the Polar manager and a TableDataHandler to send store the data of
* the phone sensors and send it to a Kafka REST proxy.
*/
class PolarService : SourceService<PolarState>() {
private lateinit var handler: SafeHandler

override val defaultState: PolarState
get() = PolarState()

private lateinit var sharedPreferences: SharedPreferences

override fun onCreate() {
super.onCreate()
handler = SafeHandler.getInstance("Polar", Process.THREAD_PRIORITY_FOREGROUND)
sharedPreferences = getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
}

override fun createSourceManager() = PolarManager(this)
Expand All @@ -31,17 +31,16 @@ class PolarService : SourceService<PolarState>() {
manager as PolarManager
}

fun savePolarDevice(deviceId: String) {
applicationContext.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)

fun savePolarDevice(deviceId: String) =
sharedPreferences
.edit()
.putString(SHARED_PREF_KEY, deviceId)
.apply()
}

fun getPolarDevice(): String? {
return applicationContext.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
fun getPolarDevice(): String? =
sharedPreferences
.getString(SHARED_PREF_KEY, null)
}

companion object {
val SHARED_PREF_NAME: String = PolarService::class.java.name
Expand Down

0 comments on commit 6c9c0e6

Please sign in to comment.