Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
34 changes: 34 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,40 @@
<uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.READ_VO2_MAX" />
<uses-permission android:name="android.permission.health.READ_WEIGHT" />
<uses-permission android:name="android.permission.health.READ_EXERCISE" />
<uses-permission android:name="android.permission.health.READ_SPEED" />
<uses-permission android:name="android.permission.health.READ_POWER" />
<uses-permission android:name="android.permission.health.READ_CYCLING_PEDALING_CADENCE" />

<uses-permission android:name="android.permission.health.WRITE_ACTIVE_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.WRITE_BASAL_BODY_TEMPERATURE" />
<uses-permission android:name="android.permission.health.WRITE_BASAL_METABOLIC_RATE" />
<uses-permission android:name="android.permission.health.WRITE_BLOOD_GLUCOSE" />
<uses-permission android:name="android.permission.health.WRITE_BLOOD_PRESSURE" />
<uses-permission android:name="android.permission.health.WRITE_BODY_FAT" />
<uses-permission android:name="android.permission.health.WRITE_BODY_TEMPERATURE" />
<uses-permission android:name="android.permission.health.WRITE_BODY_WATER_MASS" />
<uses-permission android:name="android.permission.health.WRITE_BONE_MASS" />
<uses-permission android:name="android.permission.health.WRITE_DISTANCE" />
<uses-permission android:name="android.permission.health.WRITE_ELEVATION_GAINED" />
<uses-permission android:name="android.permission.health.WRITE_FLOORS_CLIMBED" />
<uses-permission android:name="android.permission.health.WRITE_HEART_RATE" />
<uses-permission android:name="android.permission.health.WRITE_HEART_RATE_VARIABILITY" />
<uses-permission android:name="android.permission.health.WRITE_HEIGHT" />
<uses-permission android:name="android.permission.health.WRITE_HYDRATION" />
<uses-permission android:name="android.permission.health.WRITE_LEAN_BODY_MASS" />
<uses-permission android:name="android.permission.health.WRITE_OXYGEN_SATURATION" />
<uses-permission android:name="android.permission.health.WRITE_RESPIRATORY_RATE" />
<uses-permission android:name="android.permission.health.WRITE_RESTING_HEART_RATE" />
<uses-permission android:name="android.permission.health.WRITE_SLEEP" />
<uses-permission android:name="android.permission.health.WRITE_STEPS" />
<uses-permission android:name="android.permission.health.WRITE_TOTAL_CALORIES_BURNED" />
<uses-permission android:name="android.permission.health.WRITE_VO2_MAX" />
<uses-permission android:name="android.permission.health.WRITE_WEIGHT" />
<uses-permission android:name="android.permission.health.WRITE_EXERCISE" />
<uses-permission android:name="android.permission.health.WRITE_SPEED" />
<uses-permission android:name="android.permission.health.WRITE_POWER" />
<uses-permission android:name="android.permission.health.WRITE_CYCLING_PEDALING_CADENCE" />


<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ import io.homeassistant.companion.android.database.settings.WebsocketSetting
import io.homeassistant.companion.android.sensors.LocationSensorManager
import io.homeassistant.companion.android.sensors.NotificationSensorManager
import io.homeassistant.companion.android.sensors.SensorReceiver
import io.homeassistant.companion.android.sensors.healthconnect.HealthConnectWriteResult
import io.homeassistant.companion.android.sensors.healthconnect.command.HealthConnectWriteCommandHandler
import io.homeassistant.companion.android.settings.SettingsActivity
import io.homeassistant.companion.android.settings.assist.AssistConfigManager
import io.homeassistant.companion.android.settings.assist.DefaultAssistantManager
Expand Down Expand Up @@ -125,6 +127,7 @@ class MessagingManager @Inject constructor(
private val permissionRequestMediator: PermissionRequestMediator,
private val assistConfigManager: AssistConfigManager,
private val defaultAssistantManager: DefaultAssistantManager,
private val healthConnectWriteCommandHandler: HealthConnectWriteCommandHandler,
) {
companion object {
const val APP_PREFIX = "app://"
Expand Down Expand Up @@ -190,6 +193,7 @@ class MessagingManager @Inject constructor(
const val COMMAND_FLASHLIGHT = "command_flashlight"

const val COMMAND_WAKE_WORD_DETECTION = "command_wake_word_detection"
const val COMMAND_HEALTH_CONNECT_WRITE = "command_health_connect_write"

// DND commands
const val DND_PRIORITY_ONLY = "priority_only"
Expand Down Expand Up @@ -247,6 +251,7 @@ class MessagingManager @Inject constructor(
COMMAND_SCREEN_OFF_TIMEOUT,
COMMAND_FLASHLIGHT,
COMMAND_WAKE_WORD_DETECTION,
COMMAND_HEALTH_CONNECT_WRITE,
)
val DND_COMMANDS = listOf(DND_ALARMS_ONLY, DND_ALL, DND_NONE, DND_PRIORITY_ONLY)
val RM_COMMANDS = listOf(RM_NORMAL, RM_SILENT, RM_VIBRATE)
Expand Down Expand Up @@ -615,6 +620,10 @@ class MessagingManager @Inject constructor(
}
}

COMMAND_HEALTH_CONNECT_WRITE -> {
handleHealthConnectWriteCommand(jsonData)
}

else -> Timber.d("No command received")
}
}
Expand Down Expand Up @@ -644,6 +653,41 @@ class MessagingManager @Inject constructor(
}
}

/**
* Forwards a `command_health_connect_write` payload to
* [HealthConnectWriteCommandHandler] and surfaces a notification when the write
* fails so an HA-side automation author can see why their write was dropped.
*
* The success path is intentionally silent — repeating an HA-driven write back
* to the user as a notification would be noise. Permission and validation
* errors do produce notifications because they almost always require user
* action (granting a WRITE permission, fixing the FCM payload).
*
* The fallback notification rewrites `title`/`message` so the user sees the
* actual failure reason (missing permission, invalid payload, …) instead of the
* literal `command_health_connect_write` string from the inbound FCM data.
*/
private suspend fun handleHealthConnectWriteCommand(data: Map<String, String>) {
val result = healthConnectWriteCommandHandler.handle(data)
val (title, body) = when (result) {
is HealthConnectWriteResult.Success,
is HealthConnectWriteResult.Unavailable,
-> return
is HealthConnectWriteResult.MissingPermission ->
"Health Connect write blocked" to
"Missing permission: ${result.permission}. Grant it from Settings → Sensors → Health Connect."
is HealthConnectWriteResult.InvalidPayload ->
"Health Connect write rejected" to result.reason
is HealthConnectWriteResult.Failure ->
"Health Connect write failed" to (result.cause.message ?: result.cause.javaClass.simpleName)
}
val rewritten = data.toMutableMap().apply {
put(NotificationData.TITLE, title)
put(NotificationData.MESSAGE, body)
}
sendNotification(rewritten)
}

private suspend fun handleDeviceCommands(data: Map<String, String>) {
val message = data[NotificationData.MESSAGE]
val command = data[NotificationData.COMMAND]
Expand Down
Loading
Loading