mirror of
https://github.com/usetrmnl/trmnl-android.git
synced 2026-04-29 13:35:26 -07:00
feat: migrate battery reporting to Percent-Charged header
Replace complex user-level battery reporting (PATCH /api/devices/{id}) with
simplified device-level Percent-Charged header sent with image fetch requests.
Changes:
- Add percentCharged parameter to TrmnlApiService.getNextDisplayData()
- Update TrmnlDisplayRepository to send battery for BYOD devices only
- Deprecate reportDeviceBatteryStatus() and getDeviceIdFromApi() methods
- Remove battery reporting call from TrmnlImageRefreshWorker
- Disable user API token UI in AppSettingsScreen (100+ lines)
- Deprecate TrmnlUserApiService and TrmnlDeviceUpdateRequest
- Deprecate userApiToken and deviceId in TrmnlDeviceConfig
- Deprecate DataStore methods for user token and device ID
- Add 4 new battery percentage header tests
- Ignore 6 deprecated battery/device ID tests
- Update existing RSSI tests to include percentCharged parameter
Benefits:
- Simpler: No separate API call needed
- Secure: Uses device-level auth only (no user token required)
- Consistent: Follows same pattern as RSSI header
- BYOD-only: Battery reporting limited to BYOD devices as designed
All changes maintain backward compatibility with deprecated code preserved.
Verified with:
- formatKotlin: ✅ (0 errors)
- lintKotlin: ✅ (0 errors)
- testDebugUnitTest: ✅ (203 passed, 9 skipped)
- assembleDebug: ✅ (APK built successfully)
This commit is contained in:
@@ -361,7 +361,14 @@ class TrmnlDeviceConfigDataStore
|
||||
|
||||
/**
|
||||
* Saves the user-level API token (Account API key)
|
||||
*
|
||||
* **DEPRECATED:** User API token is no longer needed for battery reporting.
|
||||
* Battery percentage is now sent via the Percent-Charged header in /api/display call.
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "No longer needed for battery reporting. Battery is now sent via Percent-Charged header.",
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
suspend fun saveUserApiToken(token: String) {
|
||||
Timber.tag(TAG).d("Saving user API token: ${token.obfuscated()}")
|
||||
context.deviceConfigStore.edit { preferences ->
|
||||
@@ -372,7 +379,14 @@ class TrmnlDeviceConfigDataStore
|
||||
|
||||
/**
|
||||
* Gets the user-level API token
|
||||
*
|
||||
* **DEPRECATED:** User API token is no longer needed for battery reporting.
|
||||
* Battery percentage is now sent via the Percent-Charged header in /api/display call.
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "No longer needed for battery reporting. Battery is now sent via Percent-Charged header.",
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
suspend fun getUserApiToken(): String? {
|
||||
val token =
|
||||
context.deviceConfigStore.data
|
||||
@@ -385,8 +399,15 @@ class TrmnlDeviceConfigDataStore
|
||||
/**
|
||||
* Saves the device ID (TRMNL device ID from /api/devices/me).
|
||||
*
|
||||
* **DEPRECATED:** Device ID is no longer needed for battery reporting.
|
||||
* Battery percentage is now sent via the Percent-Charged header in /api/display call.
|
||||
*
|
||||
* **Note:** This is only applicable for BYOD device types.
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "No longer needed for battery reporting. Battery is now sent via Percent-Charged header.",
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
suspend fun saveDeviceId(deviceId: Int) {
|
||||
Timber.tag(TAG).d("Saving device ID: $deviceId")
|
||||
context.deviceConfigStore.edit { preferences ->
|
||||
@@ -398,8 +419,15 @@ class TrmnlDeviceConfigDataStore
|
||||
/**
|
||||
* Gets the device ID.
|
||||
*
|
||||
* **DEPRECATED:** Device ID is no longer needed for battery reporting.
|
||||
* Battery percentage is now sent via the Percent-Charged header in /api/display call.
|
||||
*
|
||||
* **Note:** This is only applicable for BYOD device types.
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "No longer needed for battery reporting. Battery is now sent via Percent-Charged header.",
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
suspend fun getDeviceId(): Int? {
|
||||
val deviceId =
|
||||
context.deviceConfigStore.data
|
||||
|
||||
@@ -15,11 +15,8 @@ import ink.trmnl.android.network.TrmnlApiService.Companion.CURRENT_PLAYLIST_SCRE
|
||||
import ink.trmnl.android.network.TrmnlApiService.Companion.MODELS_API_PATH
|
||||
import ink.trmnl.android.network.TrmnlApiService.Companion.NEXT_PLAYLIST_SCREEN_API_PATH
|
||||
import ink.trmnl.android.network.TrmnlUserApiService
|
||||
import ink.trmnl.android.network.TrmnlUserApiService.Companion.DEVICE_API_PATH
|
||||
import ink.trmnl.android.network.TrmnlUserApiService.Companion.USER_INFO_API_PATH
|
||||
import ink.trmnl.android.network.model.TrmnlDevice
|
||||
import ink.trmnl.android.network.model.TrmnlDeviceModel
|
||||
import ink.trmnl.android.network.model.TrmnlDeviceUpdateRequest
|
||||
import ink.trmnl.android.network.model.TrmnlDisplayResponse
|
||||
import ink.trmnl.android.network.model.TrmnlUser
|
||||
import ink.trmnl.android.network.util.constructApiUrl
|
||||
@@ -82,6 +79,13 @@ class TrmnlDisplayRepository
|
||||
} else {
|
||||
null
|
||||
},
|
||||
percentCharged =
|
||||
if (trmnlDeviceConfig.type == TrmnlDeviceType.BYOD) {
|
||||
// Send battery percentage if available for BYOD devices only
|
||||
androidDeviceInfoProvider.getBatteryLevel()?.toDouble()
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
|
||||
when (result) {
|
||||
@@ -384,6 +388,12 @@ class TrmnlDisplayRepository
|
||||
/**
|
||||
* Fetches the device ID from the TRMNL API using the device API token.
|
||||
*
|
||||
* **DEPRECATED:** Device ID fetching is no longer needed. Battery reporting now uses
|
||||
* the Percent-Charged header in /api/display call, which only requires device-level
|
||||
* authentication (Access-Token), not user-level authentication or device ID.
|
||||
*
|
||||
* This method will be removed in a future version.
|
||||
*
|
||||
* This method calls the /api/devices/me endpoint with device-level authentication
|
||||
* to retrieve device information including the device ID, which is needed for
|
||||
* user-level API calls to /api/devices/{id}.
|
||||
@@ -394,7 +404,17 @@ class TrmnlDisplayRepository
|
||||
* @param config Device configuration containing the device API token
|
||||
* @return A Result containing the device ID on success or an exception on failure
|
||||
*/
|
||||
@Deprecated("Device ID no longer needed for battery reporting. Use Percent-Charged header instead.")
|
||||
suspend fun getDeviceIdFromApi(config: TrmnlDeviceConfig): Result<Int> {
|
||||
Timber.w("getDeviceIdFromApi is deprecated. Device ID is no longer needed for battery reporting.")
|
||||
return Result.failure(
|
||||
UnsupportedOperationException(
|
||||
"Device ID fetching is deprecated. Battery reporting now uses Percent-Charged header " +
|
||||
"in /api/display, which doesn't require device ID or user API token.",
|
||||
),
|
||||
)
|
||||
|
||||
/* DISABLED - Device ID no longer needed for battery reporting
|
||||
Timber.i("Fetching device ID from API for device type: ${config.type}")
|
||||
|
||||
// Always use mocked response since the endpoint doesn't exist yet
|
||||
@@ -438,20 +458,29 @@ class TrmnlDisplayRepository
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the device's battery status to the TRMNL API for BYOD devices.
|
||||
*
|
||||
* This is a convenience method that checks if the device is a BYOD device with the necessary
|
||||
* configuration (deviceId and userApiToken), retrieves the current battery level,
|
||||
* and reports it to the server.
|
||||
* **DEPRECATED:** Battery reporting now uses the Percent-Charged header in /api/display call.
|
||||
* This method is disabled and will be removed in a future version.
|
||||
*
|
||||
* This method should be called after successful image refresh operations.
|
||||
* Battery percentage is now automatically sent via the Percent-Charged header parameter
|
||||
* when calling getNextDisplayData() for BYOD devices, eliminating the need for a separate
|
||||
* API call and user-level authentication.
|
||||
*
|
||||
* @param config Device configuration containing device type, device ID, and user API token
|
||||
* @see getNextDisplayData
|
||||
*/
|
||||
@Deprecated("Battery reporting now uses Percent-Charged header. This method is no longer needed.")
|
||||
suspend fun reportDeviceBatteryStatus(config: TrmnlDeviceConfig) {
|
||||
// DEPRECATED: Battery reporting now happens via Percent-Charged header in /api/display
|
||||
Timber.d("Battery reporting via separate API call is deprecated. Battery is now sent via Percent-Charged header.")
|
||||
return
|
||||
|
||||
/* DISABLED - Battery now reported via Percent-Charged header
|
||||
// Only report battery for BYOD devices with required configuration
|
||||
if (config.type != TrmnlDeviceType.BYOD) {
|
||||
Timber.d("Battery reporting skipped: not a BYOD device (type: ${config.type})")
|
||||
@@ -484,25 +513,35 @@ class TrmnlDisplayRepository
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Unexpected error during battery reporting")
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the device's battery status to the TRMNL API.
|
||||
*
|
||||
* This method sends a PATCH request to /api/devices/{id} using user-level authentication
|
||||
* to update the device's battery percentage on the server.
|
||||
* **DEPRECATED:** This method is no longer used. Battery reporting now uses the
|
||||
* Percent-Charged header in /api/display call instead of PATCH /api/devices/{id}.
|
||||
*
|
||||
* This suspend function performs network I/O and should be called from a background
|
||||
* coroutine so it does not block or delay display updates.
|
||||
* This method will be removed in a future version.
|
||||
*
|
||||
* @param config Device configuration containing the device ID and user API token
|
||||
* @param batteryPercent The current battery percentage (0-100)
|
||||
* @return A Result containing Unit on success or an exception on failure
|
||||
*/
|
||||
@Deprecated("Battery reporting now uses Percent-Charged header. This method is no longer needed.")
|
||||
private suspend fun reportBatteryStatus(
|
||||
config: TrmnlDeviceConfig,
|
||||
batteryPercent: Int,
|
||||
): Result<Unit> {
|
||||
// DEPRECATED: This method is no longer used
|
||||
Timber.d("reportBatteryStatus is deprecated and disabled")
|
||||
return Result.failure(
|
||||
UnsupportedOperationException(
|
||||
"Battery reporting via PATCH /api/devices/{id} is deprecated. Use Percent-Charged header instead.",
|
||||
),
|
||||
)
|
||||
|
||||
/* DISABLED - Battery now reported via Percent-Charged header
|
||||
val deviceId = config.deviceId
|
||||
val userApiToken = config.userApiToken
|
||||
|
||||
@@ -545,5 +584,6 @@ class TrmnlDisplayRepository
|
||||
Result.success(Unit)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,13 +35,31 @@ data class TrmnlDeviceConfig constructor(
|
||||
val isMasterDevice: Boolean? = null,
|
||||
/**
|
||||
* User-level API token (Account API key) for user-level endpoints.
|
||||
*
|
||||
* **DEPRECATED:** This field is no longer needed for battery reporting.
|
||||
* Battery percentage is now sent via the Percent-Charged header in /api/display call,
|
||||
* which only requires device-level authentication ([apiAccessToken]).
|
||||
*
|
||||
* This field is kept for backward compatibility and may be removed in a future version.
|
||||
*
|
||||
* Required for BYOD devices to access user-level API endpoints like /api/me and /api/devices.
|
||||
*
|
||||
* This is separate from [apiAccessToken] which is the device-level API key.
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "No longer needed for battery reporting. Battery is now sent via Percent-Charged header.",
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
val userApiToken: String? = null,
|
||||
/**
|
||||
* TRMNL device ID extracted from /api/devices/me endpoint.
|
||||
*
|
||||
* **DEPRECATED:** This field is no longer needed for battery reporting.
|
||||
* Battery percentage is now sent via the Percent-Charged header in /api/display call,
|
||||
* which doesn't require device ID or user-level authentication.
|
||||
*
|
||||
* This field is kept for backward compatibility and may be removed in a future version.
|
||||
*
|
||||
* Used for making user-level API calls to /api/devices/{id}.
|
||||
*
|
||||
* This ID is fetched during BYOD device validation and is required for
|
||||
@@ -49,5 +67,9 @@ data class TrmnlDeviceConfig constructor(
|
||||
*
|
||||
* **Note:** This field is only applicable for BYOD device types.
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "No longer needed for battery reporting. Battery is now sent via Percent-Charged header.",
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
val deviceId: Int? = null,
|
||||
)
|
||||
|
||||
@@ -94,6 +94,7 @@ interface TrmnlApiService {
|
||||
* @param deviceMacId The device's MAC address (optional)
|
||||
* @param useBase64 Whether to request Base64-encoded image data (optional)
|
||||
* @param rssi WiFi signal strength in dBm (optional, -100 to 0). See https://github.com/usetrmnl/trmnl-firmware/blob/main/src/api-client/display.cpp for additional references.
|
||||
* @param percentCharged Battery percentage (optional, 0.0 to 100.0). Only sent for BYOD devices.
|
||||
*
|
||||
* @see getCurrentDisplayData
|
||||
*/
|
||||
@@ -104,6 +105,7 @@ interface TrmnlApiService {
|
||||
@Header("ID") deviceMacId: String? = null,
|
||||
@Header("BASE64") useBase64: Boolean? = null,
|
||||
@Header("RSSI") rssi: Int? = null,
|
||||
@Header("Percent-Charged") percentCharged: Double? = null,
|
||||
): ApiResult<TrmnlDisplayResponse, Unit>
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,12 @@ import retrofit2.http.Url
|
||||
/**
|
||||
* API service interface for TRMNL user-level (account) API endpoints.
|
||||
*
|
||||
* **DEPRECATED:** This service is no longer needed for battery reporting.
|
||||
* Battery percentage is now sent via the Percent-Charged header in /api/display call,
|
||||
* which only requires device-level authentication (Access-Token).
|
||||
*
|
||||
* This interface is kept for backward compatibility and may be removed in a future version.
|
||||
*
|
||||
* This interface defines endpoints that require user-level authentication via Bearer token
|
||||
* (Account API key), as opposed to device-level authentication.
|
||||
*
|
||||
@@ -21,6 +27,10 @@ import retrofit2.http.Url
|
||||
* - https://docs.trmnl.com/go
|
||||
* - https://trmnl.com/api-docs/index.html (OpenAPI documentation)
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "No longer needed for battery reporting. Battery is now sent via Percent-Charged header.",
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
interface TrmnlUserApiService {
|
||||
companion object {
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,11 @@ import com.squareup.moshi.JsonClass
|
||||
/**
|
||||
* Data class representing a request to update a TRMNL device.
|
||||
*
|
||||
* **DEPRECATED:** This model is no longer needed for battery reporting.
|
||||
* Battery percentage is now sent via the Percent-Charged header in /api/display call.
|
||||
*
|
||||
* This class is kept for backward compatibility and may be removed in a future version.
|
||||
*
|
||||
* All fields are optional - only include the fields you want to update.
|
||||
*
|
||||
* Sample JSON request:
|
||||
@@ -24,6 +29,10 @@ import com.squareup.moshi.JsonClass
|
||||
* @property percentCharged The battery percentage charged.
|
||||
* @see ink.trmnl.android.network.TrmnlApiService.updateDevice
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "No longer needed for battery reporting. Battery is now sent via Percent-Charged header.",
|
||||
level = DeprecationLevel.WARNING,
|
||||
)
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class TrmnlDeviceUpdateRequest(
|
||||
@Json(name = "sleep_mode_enabled") val sleepModeEnabled: Boolean? = null,
|
||||
|
||||
@@ -391,6 +391,10 @@ class AppSettingsPresenter
|
||||
}
|
||||
|
||||
AppSettingsScreen.Event.ValidateUserToken -> {
|
||||
// DEPRECATED: User API token validation is no longer needed
|
||||
// Battery reporting now uses Percent-Charged header instead of user-level API
|
||||
Timber.d("User token validation skipped - no longer needed for battery reporting")
|
||||
/* DISABLED - User token no longer needed for battery reporting
|
||||
scope.launch {
|
||||
focusManager.clearFocus()
|
||||
isLoading = true
|
||||
@@ -440,6 +444,7 @@ class AppSettingsPresenter
|
||||
|
||||
isLoading = false
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
AppSettingsScreen.Event.ValidateToken -> {
|
||||
@@ -514,6 +519,10 @@ class AppSettingsPresenter
|
||||
response.refreshIntervalSeconds ?: DEFAULT_REFRESH_INTERVAL_SEC,
|
||||
)
|
||||
|
||||
// DEPRECATED: Device ID fetching no longer needed
|
||||
// Battery reporting now uses Percent-Charged header instead of user-level API
|
||||
|
||||
/* DISABLED - Device ID no longer needed for battery reporting
|
||||
// For BYOD devices, also fetch and save the device ID
|
||||
if (deviceType == TrmnlDeviceType.BYOD) {
|
||||
val deviceIdResult = displayRepository.getDeviceIdFromApi(deviceConfig)
|
||||
@@ -530,6 +539,7 @@ class AppSettingsPresenter
|
||||
)
|
||||
}
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
// No error but also no image URL
|
||||
val errorMessage = response.error ?: ""
|
||||
@@ -813,6 +823,17 @@ fun AppSettingsContent(
|
||||
deviceIdError = (state.validationResult as? ValidationResult.InvalidDeviceMacId)?.message,
|
||||
)
|
||||
|
||||
//
|
||||
// DEPRECATED: User API Token field is no longer needed
|
||||
//
|
||||
// Battery reporting now uses the Percent-Charged header in /api/display call,
|
||||
// which only requires device-level authentication (Access-Token).
|
||||
// User-level authentication is no longer needed for BYOD device battery reporting.
|
||||
//
|
||||
// This UI has been disabled but kept in code for reference.
|
||||
//
|
||||
|
||||
/* DISABLED - User API token no longer needed for battery reporting
|
||||
// User API Token field (only for BYOD)
|
||||
AnimatedVisibility(
|
||||
visible = state.deviceType == TrmnlDeviceType.BYOD,
|
||||
@@ -908,6 +929,7 @@ fun AppSettingsContent(
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
|
||||
@@ -170,8 +170,9 @@ class TrmnlImageRefreshWorker(
|
||||
httpResponseMetadata = trmnlDisplayInfo.httpResponseMetadata,
|
||||
)
|
||||
|
||||
// Report battery status for BYOD devices after successful image refresh
|
||||
displayRepository.reportDeviceBatteryStatus(deviceConfig)
|
||||
// DEPRECATED: Battery reporting now happens via Percent-Charged header in /api/display call
|
||||
// Battery percentage is automatically sent when fetching the next image for BYOD devices
|
||||
// displayRepository.reportDeviceBatteryStatus(deviceConfig)
|
||||
|
||||
// NOTE: Image metadata caching is handled automatically by `TrmnlDisplayRepository`
|
||||
// when the API call succeeds, so we don't need to save it again here.
|
||||
|
||||
@@ -21,6 +21,7 @@ import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
@@ -410,6 +411,8 @@ class TrmnlDisplayRepositoryTest {
|
||||
accessToken = byodDeviceConfig.apiAccessToken,
|
||||
useBase64 = any(),
|
||||
rssi = any(),
|
||||
percentCharged = any(),
|
||||
deviceMacId = any(),
|
||||
)
|
||||
} returns ApiResult.success(successResponse)
|
||||
|
||||
@@ -426,8 +429,10 @@ class TrmnlDisplayRepositoryTest {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = expectedNextApiUrl,
|
||||
accessToken = byodDeviceConfig.apiAccessToken,
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = any(),
|
||||
percentCharged = any(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -579,6 +584,9 @@ class TrmnlDisplayRepositoryTest {
|
||||
assertThat(result.httpResponseMetadata?.contentLength).isEqualTo(123L)
|
||||
}
|
||||
|
||||
// DEPRECATED: Device ID fetching is no longer needed for battery reporting
|
||||
// Battery is now sent via Percent-Charged header
|
||||
@Ignore("Device ID fetching deprecated - battery now uses Percent-Charged header")
|
||||
@Test
|
||||
fun `getDeviceIdFromApi should return mocked device ID`() =
|
||||
runTest {
|
||||
@@ -593,6 +601,9 @@ class TrmnlDisplayRepositoryTest {
|
||||
coVerify(exactly = 0) { apiService.getDeviceMe(any(), any()) }
|
||||
}
|
||||
|
||||
// DEPRECATED: Battery reporting via separate API call is no longer used
|
||||
// Battery is now sent via Percent-Charged header in /api/display call
|
||||
@Ignore("Battery reporting via PATCH /api/devices/{id} deprecated - now uses Percent-Charged header")
|
||||
@Test
|
||||
fun `reportDeviceBatteryStatus should report battery for valid BYOD config`() =
|
||||
runTest {
|
||||
@@ -628,6 +639,8 @@ class TrmnlDisplayRepositoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
// DEPRECATED: Battery reporting via separate API call is no longer used
|
||||
@Ignore("Battery reporting via PATCH /api/devices/{id} deprecated - now uses Percent-Charged header")
|
||||
@Test
|
||||
fun `reportDeviceBatteryStatus should skip for non-BYOD device`() =
|
||||
runTest {
|
||||
@@ -646,6 +659,8 @@ class TrmnlDisplayRepositoryTest {
|
||||
coVerify(exactly = 0) { androidDeviceInfoProvider.getBatteryLevel() }
|
||||
}
|
||||
|
||||
// DEPRECATED: Battery reporting via separate API call is no longer used
|
||||
@Ignore("Battery reporting via PATCH /api/devices/{id} deprecated - now uses Percent-Charged header")
|
||||
@Test
|
||||
fun `reportDeviceBatteryStatus should skip when deviceId is null`() =
|
||||
runTest {
|
||||
@@ -664,6 +679,8 @@ class TrmnlDisplayRepositoryTest {
|
||||
coVerify(exactly = 0) { androidDeviceInfoProvider.getBatteryLevel() }
|
||||
}
|
||||
|
||||
// DEPRECATED: Battery reporting via separate API call is no longer used
|
||||
@Ignore("Battery reporting via PATCH /api/devices/{id} deprecated - now uses Percent-Charged header")
|
||||
@Test
|
||||
fun `reportDeviceBatteryStatus should skip when userApiToken is null`() =
|
||||
runTest {
|
||||
@@ -682,6 +699,8 @@ class TrmnlDisplayRepositoryTest {
|
||||
coVerify(exactly = 0) { androidDeviceInfoProvider.getBatteryLevel() }
|
||||
}
|
||||
|
||||
// DEPRECATED: Battery reporting via separate API call is no longer used
|
||||
@Ignore("Battery reporting via PATCH /api/devices/{id} deprecated - now uses Percent-Charged header")
|
||||
@Test
|
||||
fun `reportDeviceBatteryStatus should skip when battery level unavailable`() =
|
||||
runTest {
|
||||
@@ -715,8 +734,10 @@ class TrmnlDisplayRepositoryTest {
|
||||
apiAccessToken = "test_api_key",
|
||||
)
|
||||
val expectedRssi = -65
|
||||
val expectedBattery = 80
|
||||
|
||||
every { androidDeviceInfoProvider.getWifiSignalStrength() } returns expectedRssi
|
||||
every { androidDeviceInfoProvider.getBatteryLevel() } returns expectedBattery
|
||||
|
||||
coEvery {
|
||||
apiService.getNextDisplayData(
|
||||
@@ -725,6 +746,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = expectedRssi,
|
||||
percentCharged = expectedBattery.toDouble(),
|
||||
)
|
||||
} returns ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
@@ -740,6 +762,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = expectedRssi,
|
||||
percentCharged = expectedBattery.toDouble(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -754,10 +777,12 @@ class TrmnlDisplayRepositoryTest {
|
||||
userApiToken = "test_token",
|
||||
apiAccessToken = "test_api_key",
|
||||
)
|
||||
val expectedBattery = 80
|
||||
|
||||
every { androidDeviceInfoProvider.getWifiSignalStrength() } returns null
|
||||
every { androidDeviceInfoProvider.getBatteryLevel() } returns expectedBattery
|
||||
|
||||
coEvery { apiService.getNextDisplayData(any(), any(), any(), any()) } returns
|
||||
coEvery { apiService.getNextDisplayData(any(), any(), any(), any(), any(), any()) } returns
|
||||
ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
// Act
|
||||
@@ -772,6 +797,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = null,
|
||||
percentCharged = expectedBattery.toDouble(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -785,7 +811,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
apiAccessToken = "trmnl_api_key",
|
||||
)
|
||||
|
||||
coEvery { apiService.getNextDisplayData(any(), any(), any(), any()) } returns
|
||||
coEvery { apiService.getNextDisplayData(any(), any(), any(), any(), any(), any()) } returns
|
||||
ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
// Act
|
||||
@@ -800,6 +826,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = null,
|
||||
percentCharged = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -833,6 +860,164 @@ class TrmnlDisplayRepositoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
// Battery Percentage (Percent-Charged Header) Tests
|
||||
|
||||
@Test
|
||||
fun `getNextDisplayData should send battery percentage for BYOD device when available`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val byodConfig =
|
||||
byodDeviceConfig.copy(
|
||||
apiAccessToken = "test_api_key",
|
||||
)
|
||||
val expectedBatteryLevel = 75
|
||||
|
||||
every { androidDeviceInfoProvider.getBatteryLevel() } returns expectedBatteryLevel
|
||||
every { androidDeviceInfoProvider.getWifiSignalStrength() } returns -65
|
||||
|
||||
coEvery {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = any(),
|
||||
accessToken = any(),
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = any(),
|
||||
percentCharged = 75.0,
|
||||
)
|
||||
} returns ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
// Act
|
||||
repository.getNextDisplayData(byodConfig)
|
||||
|
||||
// Assert - Verify battery level was fetched and sent as header
|
||||
coVerify(exactly = 1) { androidDeviceInfoProvider.getBatteryLevel() }
|
||||
coVerify {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = any(),
|
||||
accessToken = any(),
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = any(),
|
||||
percentCharged = 75.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getNextDisplayData should send null battery percentage for BYOD when unavailable`() =
|
||||
runTest {
|
||||
// Arrange
|
||||
val byodConfig =
|
||||
byodDeviceConfig.copy(
|
||||
apiAccessToken = "test_api_key",
|
||||
)
|
||||
|
||||
every { androidDeviceInfoProvider.getBatteryLevel() } returns null
|
||||
every { androidDeviceInfoProvider.getWifiSignalStrength() } returns -65
|
||||
|
||||
coEvery {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = any(),
|
||||
accessToken = any(),
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = any(),
|
||||
percentCharged = null,
|
||||
)
|
||||
} returns ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
// Act
|
||||
repository.getNextDisplayData(byodConfig)
|
||||
|
||||
// Assert - Verify battery level was fetched but null was sent
|
||||
coVerify(exactly = 1) { androidDeviceInfoProvider.getBatteryLevel() }
|
||||
coVerify {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = any(),
|
||||
accessToken = any(),
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = any(),
|
||||
percentCharged = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getNextDisplayData should NOT send battery percentage for TRMNL device`() =
|
||||
runTest {
|
||||
// Arrange - TRMNL device (not BYOD)
|
||||
val trmnlConfig =
|
||||
testDeviceConfig.copy(
|
||||
apiAccessToken = "trmnl_api_key",
|
||||
)
|
||||
|
||||
coEvery {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = any(),
|
||||
accessToken = any(),
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = null,
|
||||
percentCharged = null,
|
||||
)
|
||||
} returns ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
// Act
|
||||
repository.getNextDisplayData(trmnlConfig)
|
||||
|
||||
// Assert - Verify battery level was NOT fetched and null was sent
|
||||
coVerify(exactly = 0) { androidDeviceInfoProvider.getBatteryLevel() }
|
||||
coVerify {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = any(),
|
||||
accessToken = any(),
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = null,
|
||||
percentCharged = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getNextDisplayData should NOT send battery percentage for BYOS device`() =
|
||||
runTest {
|
||||
// Arrange - BYOS device
|
||||
val byosConfig =
|
||||
byosDeviceConfig.copy(
|
||||
apiAccessToken = "byos_api_key",
|
||||
)
|
||||
|
||||
coEvery {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = any(),
|
||||
accessToken = any(),
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = null,
|
||||
percentCharged = null,
|
||||
)
|
||||
} returns ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
// Act
|
||||
repository.getNextDisplayData(byosConfig)
|
||||
|
||||
// Assert - Verify battery level was NOT fetched for BYOS device
|
||||
coVerify(exactly = 0) { androidDeviceInfoProvider.getBatteryLevel() }
|
||||
// Verify null battery percentage was sent
|
||||
coVerify {
|
||||
apiService.getNextDisplayData(
|
||||
fullApiUrl = any(),
|
||||
accessToken = any(),
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = null,
|
||||
percentCharged = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getNextDisplayData should call getWifiSignalStrength only for BYOD devices`() =
|
||||
runTest {
|
||||
@@ -842,8 +1027,9 @@ class TrmnlDisplayRepositoryTest {
|
||||
val byosConfig = byosDeviceConfig.copy(apiAccessToken = "byos_key")
|
||||
|
||||
every { androidDeviceInfoProvider.getWifiSignalStrength() } returns -70
|
||||
every { androidDeviceInfoProvider.getBatteryLevel() } returns 75
|
||||
|
||||
coEvery { apiService.getNextDisplayData(any(), any(), any(), any(), any()) } returns
|
||||
coEvery { apiService.getNextDisplayData(any(), any(), any(), any(), any(), any()) } returns
|
||||
ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
// Act - Fetch for all device types
|
||||
@@ -861,8 +1047,10 @@ class TrmnlDisplayRepositoryTest {
|
||||
// Arrange
|
||||
val byodConfig = byodDeviceConfig.copy(apiAccessToken = "test_key")
|
||||
val strongSignal = -30 // Excellent signal
|
||||
val expectedBattery = 80
|
||||
|
||||
every { androidDeviceInfoProvider.getWifiSignalStrength() } returns strongSignal
|
||||
every { androidDeviceInfoProvider.getBatteryLevel() } returns expectedBattery
|
||||
|
||||
coEvery {
|
||||
apiService.getNextDisplayData(
|
||||
@@ -871,6 +1059,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = strongSignal,
|
||||
percentCharged = expectedBattery.toDouble(),
|
||||
)
|
||||
} returns ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
@@ -885,6 +1074,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = strongSignal,
|
||||
percentCharged = expectedBattery.toDouble(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -895,8 +1085,10 @@ class TrmnlDisplayRepositoryTest {
|
||||
// Arrange
|
||||
val byodConfig = byodDeviceConfig.copy(apiAccessToken = "test_key")
|
||||
val weakSignal = -90 // Very weak signal
|
||||
val expectedBattery = 80
|
||||
|
||||
every { androidDeviceInfoProvider.getWifiSignalStrength() } returns weakSignal
|
||||
every { androidDeviceInfoProvider.getBatteryLevel() } returns expectedBattery
|
||||
|
||||
coEvery {
|
||||
apiService.getNextDisplayData(
|
||||
@@ -905,6 +1097,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = weakSignal,
|
||||
percentCharged = expectedBattery.toDouble(),
|
||||
)
|
||||
} returns ApiResult.success(mockk(relaxed = true))
|
||||
|
||||
@@ -919,6 +1112,7 @@ class TrmnlDisplayRepositoryTest {
|
||||
deviceMacId = any(),
|
||||
useBase64 = any(),
|
||||
rssi = weakSignal,
|
||||
percentCharged = expectedBattery.toDouble(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ paths:
|
||||
description: Device battery voltage (eg. 3.7)
|
||||
schema:
|
||||
type: number
|
||||
- name: Percent-Charged
|
||||
in: header
|
||||
required: false
|
||||
description: Device percent charged (eg. 69.4)
|
||||
schema:
|
||||
type: number
|
||||
- name: FW-Version
|
||||
in: header
|
||||
required: false
|
||||
|
||||
Reference in New Issue
Block a user