v6: fix detection send queue

Root cause: Server expects flat array of detections with device_id on each
item, but client was sending nested {device_id, detections: [...]}.

The /health endpoint worked (ADAMaps showed green) but /api/ingest failed
validation: 'Missing required fields: device_id, lat, lon' on every request.
Items queued indefinitely, sent=0.

Fixed by:
- Adding device_id field to AdaMapsDetection
- Changed AdaMapsIngestRequest to typealias for List<AdaMapsDetection>
- Updated toAdaMapsDetection() to accept deviceId parameter
- Updated ForwardingService to send flat array format
This commit is contained in:
Kayos 2026-03-11 10:37:56 -07:00
parent 2b8c19eef1
commit f06ce3f319
3 changed files with 17 additions and 18 deletions

View file

@ -53,9 +53,10 @@ class AdaMapsApiClient(
apiKey = key
}
suspend fun ingest(request: AdaMapsIngestRequest): ApiResult<String> = withContext(Dispatchers.IO) {
suspend fun ingest(detections: AdaMapsIngestRequest): ApiResult<String> = withContext(Dispatchers.IO) {
try {
val body = gson.toJson(request).toRequestBody(json)
// Server expects flat array of detections, each with device_id
val body = gson.toJson(detections).toRequestBody(json)
val req = Request.Builder()
.url("$apiUrl/api/ingest")
.addHeader("X-MapNet-Key", apiKey)

View file

@ -49,6 +49,7 @@ data class BeeDeviceInfo(
// ── ADAMaps ingest ────────────────────────────────────────────────────────────
data class AdaMapsDetection(
@SerializedName("device_id") val deviceId: String,
@SerializedName("id") val id: Long,
@SerializedName("class_label") val classLabel: String?,
@SerializedName("class_label_confidence") val classLabelConfidence: Double?,
@ -63,12 +64,12 @@ data class AdaMapsDetection(
@SerializedName("azimuth") val azimuth: Double?
)
data class AdaMapsIngestRequest(
@SerializedName("device_id") val deviceId: String,
@SerializedName("detections") val detections: List<AdaMapsDetection>
)
// Server expects flat array of detections, each with device_id
// (NOT nested {device_id, detections: [...]})
typealias AdaMapsIngestRequest = List<AdaMapsDetection>
fun BeeDetection.toAdaMapsDetection() = AdaMapsDetection(
fun BeeDetection.toAdaMapsDetection(deviceId: String) = AdaMapsDetection(
deviceId = deviceId,
id = id,
classLabel = classLabel,
classLabelConfidence = classLabelConfidence,

View file

@ -14,7 +14,7 @@ import com.adamaps.varroa.MainActivity
import com.adamaps.varroa.R
import com.adamaps.varroa.api.AdaMapsApiClient
import com.adamaps.varroa.api.BeeApiClient
import com.adamaps.varroa.data.AdaMapsIngestRequest
import com.adamaps.varroa.data.AdaMapsDetection
import com.adamaps.varroa.data.ApiResult
import com.adamaps.varroa.data.BeeDetection
import com.adamaps.varroa.data.SessionStats
@ -241,15 +241,14 @@ class ForwardingService : LifecycleService() {
private suspend fun sendToADAMaps(detections: List<BeeDetection>) {
// Get device ID from device info (best effort)
val deviceId = try {
(beeClient.getDeviceInfo() as? ApiResult.Success)?.data?.deviceId
?: (beeClient.getDeviceInfo() as? ApiResult.Success)?.data?.serial
val info = beeClient.getDeviceInfo()
(info as? ApiResult.Success)?.data?.deviceId
?: (info as? ApiResult.Success)?.data?.serial
?: "unknown"
} catch (e: Exception) { "unknown" }
val request = AdaMapsIngestRequest(
deviceId = deviceId,
detections = detections.map { it.toAdaMapsDetection() }
)
// Server expects flat array of detections, each with device_id embedded
val request = detections.map { it.toAdaMapsDetection(deviceId) }
_stats.update { it.copy(queued = it.queued + detections.size) }
@ -277,10 +276,8 @@ class ForwardingService : LifecycleService() {
}
for (item in toRetry) {
val request = AdaMapsIngestRequest(
deviceId = item.deviceId,
detections = item.detections.map { it.toAdaMapsDetection() }
)
// Server expects flat array of detections, each with device_id embedded
val request = item.detections.map { it.toAdaMapsDetection(item.deviceId) }
when (val result = adamapsClient.ingest(request)) {
is ApiResult.Success -> {