NSTreatment -> RemoteTreatment conversion

This commit is contained in:
Milos Kozak 2022-12-11 17:52:29 +01:00
parent d7a8323bc8
commit e1cc42635c
22 changed files with 400 additions and 85 deletions

View file

@ -3,12 +3,16 @@ package info.nightscout.sdk
import android.content.Context
import info.nightscout.sdk.exceptions.DateHeaderOutOfToleranceException
import info.nightscout.sdk.exceptions.InvalidAccessTokenException
import info.nightscout.sdk.exceptions.InvalidFormatNightscoutException
import info.nightscout.sdk.exceptions.TodoNightscoutException
import info.nightscout.sdk.exceptions.UnknownResponseNightscoutException
import info.nightscout.sdk.interfaces.NSAndroidClient
import info.nightscout.sdk.localmodel.Status
import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.sdk.localmodel.treatment.CreateUpdateResponse
import info.nightscout.sdk.localmodel.treatment.NSTreatment
import info.nightscout.sdk.mapper.toLocal
import info.nightscout.sdk.mapper.toRemoteTreatment
import info.nightscout.sdk.mapper.toSgv
import info.nightscout.sdk.mapper.toTreatment
import info.nightscout.sdk.networking.NetworkStackBuilder
@ -154,6 +158,22 @@ class NSAndroidClientImpl(
}
}
override suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse = callWrapper(dispatcher) {
val remoteTreatment = nsTreatment.toRemoteTreatment() ?: throw InvalidFormatNightscoutException()
val response = api.createTreatment(remoteTreatment)
if (response.isSuccessful) {
return@callWrapper CreateUpdateResponse(
identifier = response.body()?.result?.identifier ?: throw UnknownResponseNightscoutException(),
isDeduplication = response.body()?.result?.isDeduplication ?: false,
deduplicatedIdentifier = response.body()?.result?.deduplicatedIdentifier,
lastModified = response.body()?.result?.lastModified
)
} else {
throw TodoNightscoutException() // TODO: react to response errors (offline, ...)
}
}
private suspend fun <T> callWrapper(dispatcher: CoroutineDispatcher, block: suspend () -> T): T =
withContext(dispatcher) {
retry(

View file

@ -0,0 +1,3 @@
package info.nightscout.sdk.exceptions
class InvalidFormatNightscoutException : NightscoutException()

View file

@ -0,0 +1,3 @@
package info.nightscout.sdk.exceptions
class UnknownResponseNightscoutException : NightscoutException()

View file

@ -2,6 +2,7 @@ package info.nightscout.sdk.interfaces
import info.nightscout.sdk.localmodel.Status
import info.nightscout.sdk.localmodel.entry.NSSgvV3
import info.nightscout.sdk.localmodel.treatment.CreateUpdateResponse
import info.nightscout.sdk.localmodel.treatment.NSTreatment
import info.nightscout.sdk.remotemodel.LastModified
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
@ -19,4 +20,5 @@ interface NSAndroidClient {
suspend fun getSgvsNewerThan(from: Long, limit: Long): List<NSSgvV3>
suspend fun getTreatmentsModifiedSince(from: Long, limit: Long): List<NSTreatment>
suspend fun getDeviceStatusModifiedSince(from: Long): List<RemoteDeviceStatus>
suspend fun createTreatment(NsTreatment: NSTreatment): CreateUpdateResponse
}

View file

@ -0,0 +1,8 @@
package info.nightscout.sdk.localmodel.treatment
class CreateUpdateResponse(
val identifier: String?,
val isDeduplication: Boolean? = false,
val deduplicatedIdentifier: String? = null,
val lastModified: Long? = null
)

View file

@ -7,8 +7,8 @@ data class NSBolus(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,

View file

@ -8,8 +8,8 @@ data class NSBolusWizard(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,

View file

@ -7,8 +7,8 @@ data class NSCarbs(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,

View file

@ -8,8 +8,8 @@ data class NSEffectiveProfileSwitch(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,

View file

@ -7,8 +7,8 @@ data class NSExtendedBolus(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -21,5 +21,5 @@ data class NSExtendedBolus(
override val pumpSerial: String?,
val duration: Long,
val enteredinsulin: Double,
val isEmulatingTempbasal: Boolean
val isEmulatingTempBasal: Boolean?
) : NSTreatment

View file

@ -7,8 +7,8 @@ data class NSOfflineEvent(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,

View file

@ -8,8 +8,8 @@ data class NSProfileSwitch(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,

View file

@ -8,8 +8,8 @@ data class NSTemporaryBasal(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,
@ -21,9 +21,11 @@ data class NSTemporaryBasal(
override val pumpType: String?,
override val pumpSerial: String?,
val duration: Long,
val rate: Double,
val rate: Double, // when sending to NS always convertedToAbsolute(timestamp, profile)
val isAbsolute: Boolean,
val type: Type
val type: Type,
val percent: Double? = null, // when sending to NS (rate - 100)
val absolute: Double? = null // when sending to NS (rate)
) : NSTreatment {
enum class Type {

View file

@ -7,8 +7,8 @@ data class NSTemporaryTarget(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,

View file

@ -8,8 +8,8 @@ data class NSTherapyEvent(
override val device: String?,
override val identifier: String,
override val units: NsUnits?,
override val srvModified: Long,
override val srvCreated: Long,
override val srvModified: Long?,
override val srvCreated: Long?,
override val utcOffset: Long,
override val subject: String?,
override var isReadOnly: Boolean,

View file

@ -8,8 +8,8 @@ interface NSTreatment {
val identifier: String
val units: NsUnits?
val eventType: EventType
val srvModified: Long
val srvCreated: Long
val srvModified: Long?
val srvCreated: Long?
val utcOffset: Long
val subject: String?
var isReadOnly: Boolean

View file

@ -117,7 +117,7 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
pumpSerial = extendedEmulated.pumpSerial,
enteredinsulin = extendedEmulated.enteredinsulin ?: 0.0,
duration = extendedEmulated.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(extendedEmulated.duration ?: 0L),
isEmulatingTempbasal = extendedEmulated.isEmulatingTempBasal
isEmulatingTempBasal = extendedEmulated.isEmulatingTempBasal
)
}
@ -329,10 +329,270 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
pumpSerial = this.pumpSerial,
enteredinsulin = this.enteredinsulin,
duration = this.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(this.duration ?: 0L),
isEmulatingTempbasal = this.isEmulatingTempBasal
isEmulatingTempBasal = this.isEmulatingTempBasal
)
}
}
return null
}
internal fun NSTreatment.toRemoteTreatment(): RemoteTreatment? =
when (this) {
is NSBolus -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
insulin = insulin,
type = type.name
)
is NSCarbs -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
carbs = carbs,
duration = duration
)
is NSTemporaryTarget -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
targetBottom = targetBottom,
targetTop = targetTop,
reason = reason.text
)
/*
// Convert back emulated TBR -> EB
eventType == EventType.TEMPORARY_BASAL && extendedEmulated != null ->
return RemoteTreatment(
date = treatmentTimestamp,
device = device,
identifier = identifier,
units = NsUnits.fromString(extendedEmulated.units),
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset ?: 0,
subject = subject,
isReadOnly = extendedEmulated.isReadOnly ?: false,
isValid = extendedEmulated.isValid ?: true,
eventType = extendedEmulated.eventType,
notes = extendedEmulated.notes,
pumpId = extendedEmulated.pumpId,
endId = extendedEmulated.endId,
pumpType = extendedEmulated.pumpType,
pumpSerial = extendedEmulated.pumpSerial,
enteredinsulin = extendedEmulated.enteredinsulin ?: 0.0,
duration = extendedEmulated.durationInMilliseconds ?: TimeUnit.MINUTES.toMillis(extendedEmulated.duration ?: 0L),
isEmulatingTempbasal = extendedEmulated.isEmulatingTempBasal
)
}
*/
is NSTemporaryBasal -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
absolute = absolute,
percent = percent,
rate = absolute,
type = type.name
)
is NSEffectiveProfileSwitch -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
profileJson = profileJson.toString(),
originalProfileName = originalProfileName,
originalCustomizedName = originalCustomizedName,
originalTimeshift = originalTimeshift,
originalPercentage = originalPercentage,
originalDuration = originalDuration,
originalEnd = originalEnd
)
is NSProfileSwitch -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
profileJson = profileJson.toString(), // must be de-customized
profile = profileName,
originalProfileName = originalProfileName,
originalDuration = originalDuration,
duration = duration,
timeshift = timeShift,
percentage = percentage,
)
is NSBolusWizard -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
bolusCalculatorResult = bolusCalculatorResult,
glucose = glucose
)
is NSTherapyEvent -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
glucose = glucose,
enteredBy = enteredBy,
glucoseType = glucoseType?.text
)
is NSOfflineEvent -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
reason = reason.name
)
is NSExtendedBolus -> RemoteTreatment(
date = date,
device = device,
identifier = identifier,
units = units?.value,
srvModified = srvModified,
srvCreated = srvCreated,
utcOffset = utcOffset,
subject = subject,
isReadOnly = isReadOnly,
isValid = isValid,
eventType = eventType,
notes = notes,
pumpId = pumpId,
endId = endId,
pumpType = pumpType,
pumpSerial = pumpSerial,
enteredinsulin = enteredinsulin,
duration = TimeUnit.MILLISECONDS.toMinutes(duration),
durationInMilliseconds = duration,
isEmulatingTempBasal = isEmulatingTempBasal
)
else -> null
}

View file

@ -4,11 +4,17 @@ import com.google.gson.JsonElement
import info.nightscout.sdk.remotemodel.LastModified
import info.nightscout.sdk.remotemodel.RemoteDeviceStatus
import info.nightscout.sdk.remotemodel.NSResponse
import info.nightscout.sdk.remotemodel.RemoteCreateUpdateResponse
import info.nightscout.sdk.remotemodel.RemoteEntry
import info.nightscout.sdk.remotemodel.RemoteStatusResponse
import info.nightscout.sdk.remotemodel.RemoteTreatment
import okhttp3.RequestBody
import retrofit2.Call
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query
@ -48,4 +54,8 @@ internal interface NightscoutRemoteService {
@GET("v3/devicestatus/history/{from}")
suspend fun getDeviceStatusModifiedSince(@Path("from") from: Long): Response<NSResponse<List<RemoteDeviceStatus>>>
@POST("v3/treatments")
fun createTreatment(@Body remoteTreatment: RemoteTreatment): Response<NSResponse<RemoteCreateUpdateResponse>>
}

View file

@ -17,6 +17,13 @@ internal data class RemoteStorage(
@SerializedName("version") val version: String
)
internal data class RemoteCreateUpdateResponse(
@SerializedName("identifier") val identifier: String?,
@SerializedName("isDeduplication") val isDeduplication: Boolean?,
@SerializedName("deduplicatedIdentifier") val deduplicatedIdentifier: String?,
@SerializedName("lastModified") val lastModified: Long?
)
internal data class RemoteApiPermissions(
@SerializedName("devicestatus") val deviceStatus: RemoteApiPermission,
@SerializedName("entries") val entries: RemoteApiPermission,

View file

@ -18,72 +18,71 @@ import org.json.JSONObject
* */
internal data class RemoteTreatment(
@SerializedName("identifier") val identifier: String, // string Main addressing, required field that identifies document in the collection. The client should not create the identifier, the server automatically assigns it when the document is inserted.
@SerializedName("date") val date: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("mills") val mills: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix
@SerializedName("timestamp") val timestamp: Long?, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("created_at") val created_at: String, // integer($int64) or string timestamp on previous version of api, in my examples, a lot of treatments don't have date, only created_at, some of them with string others with long...
@SerializedName("utcOffset") val utcOffset: Long?, // integer Local UTC offset (timezone) of the event in minutes. This field can be set either directly by the client (in the incoming
// document) or it is automatically parsed from the date field.
@SerializedName("date") val date: Long? = null, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("mills") val mills: Long? = null, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix
@SerializedName("timestamp") val timestamp: Long? = null, // integer($int64) or string required timestamp when the record or event occurred, you can choose from three input formats Unix epoch in milliseconds (1525383610088), Unix epoch in seconds (1525383610), ISO 8601 with optional timezone ('2018-05-03T21:40:10.088Z' or '2018-05-03T23:40:10.088+02:00')
@SerializedName("created_at") val created_at: String? = null, // integer($int64) or string timestamp on previous version of api, in my examples, a lot of treatments don't have date, only created_at, some of them with string others with long...
@SerializedName("utcOffset") val utcOffset: Long? = null, // integer Local UTC offset (timezone) of the event in minutes. This field can be set either directly by the client (in the incoming document) or it is automatically parsed from the date field.
// @SerializedName("app") val app : String, // TODO required ? Application or system in which the record was entered by human or device for the first time.
@SerializedName("device") val device: String?, // string The device from which the data originated (including serial number of the device, if it is relevant and safe).
@SerializedName("srvCreated") val srvCreated: Long, // integer($int64) example: 1525383610088 The server's timestamp of document insertion into the database (Unix epoch in ms). This field appears only for documents which were inserted by API v3.
@SerializedName("subject") val subject: String?, // string Name of the security subject (within Nightscout scope) which has created the document. This field is automatically set by the server from the passed token or JWT.
@SerializedName("srvModified") val srvModified: Long, // integer($int64) example: 1525383610088 The server's timestamp of the last document modification in the database (Unix epoch in ms). This field appears only for documents which were somehow modified by API v3 (inserted, updated or deleted).
@SerializedName("modifiedBy") val modifiedBy: String?, // string Name of the security subject (within Nightscout scope) which has patched or deleted the document for the last time. This field is automatically set by the server.
@SerializedName("isValid") val isValid: Boolean?, // boolean A flag set by the server only for deleted documents. This field appears only within history operation and for documents which were deleted by API v3 (and they always have a false value)
@SerializedName("isReadOnly") val isReadOnly: Boolean?, // boolean A flag set by client that locks the document from any changes. Every document marked with isReadOnly=true is forever immutable and cannot even be deleted.
@SerializedName("device") val device: String? = null, // string The device from which the data originated (including serial number of the device, if it is relevant and safe).
@SerializedName("srvCreated") val srvCreated: Long? = null, // integer($int64) example: 1525383610088 The server's timestamp of document insertion into the database (Unix epoch in ms). This field appears only for documents which were inserted by API v3.
@SerializedName("subject") val subject: String? = null, // string Name of the security subject (within Nightscout scope) which has created the document. This field is automatically set by the server from the passed token or JWT.
@SerializedName("srvModified") val srvModified: Long? = null, // integer($int64) example: 1525383610088 The server's timestamp of the last document modification in the database (Unix epoch in ms). This field appears only for documents which were somehow modified by API v3 (inserted, updated or deleted).
@SerializedName("modifiedBy") val modifiedBy: String? = null, // string Name of the security subject (within Nightscout scope) which has patched or deleted the document for the last time. This field is automatically set by the server.
@SerializedName("isValid") val isValid: Boolean? = null, // boolean A flag set by the server only for deleted documents. This field appears only within history operation and for documents which were deleted by API v3 (and they always have a false value)
@SerializedName("isReadOnly") val isReadOnly: Boolean? = null, // boolean A flag set by client that locks the document from any changes. Every document marked with isReadOnly=true is forever immutable and cannot even be deleted.
@SerializedName("eventType") val eventType: EventType, // string "BG Check", "Snack Bolus", "Meal Bolus", "Correction Bolus", "Carb Correction", "Combo Bolus", "Announcement", "Note", "Question", "Exercise", "Site Change", "Sensor Start", "Sensor Change", "Pump Battery Change", "Insulin Change", "Temp Basal", "Profile Switch", "D.A.D. Alert", "Temporary Target", "OpenAPS Offline", "Bolus Wizard"
@SerializedName("glucose") val glucose: Double?, // double Current glucose
@SerializedName("glucoseType") val glucoseType: String?, // string example: "Sensor", "Finger", "Manual"
@SerializedName("units") val units: String?, // string The units for the glucose value, mg/dl or mmol/l. It is strongly recommended to fill in this field.
@SerializedName("carbs") val carbs: Double?, // number... Amount of carbs given.
@SerializedName("protein") val protein: Int?, // number... Amount of protein given.
@SerializedName("fat") val fat: Int?, // number... Amount of fat given.
@SerializedName("insulin") val insulin: Double?, // number... Amount of insulin, if any.
@SerializedName("duration") val duration: Long?, // number... Duration in minutes.
@SerializedName("durationInMilliseconds") val durationInMilliseconds: Long?, // number... Duration in milliseconds.
@SerializedName("preBolus") val preBolus: Int?, // number... How many minutes the bolus was given before the meal started.
@SerializedName("splitNow") val splitNow: Int?, // number... Immediate part of combo bolus (in percent).
@SerializedName("splitExt") val splitExt: Int?, // number... Extended part of combo bolus (in percent).
@SerializedName("percent") val percent: Double?, // number... Eventual basal change in percent.
@SerializedName("absolute") val absolute: Double?, // number... Eventual basal change in absolute value (insulin units per hour).
@SerializedName("targetTop") val targetTop: Double?, // number... Top limit of temporary target.
@SerializedName("targetBottom") val targetBottom: Double?, // number... Bottom limit of temporary target.
@SerializedName("profile") val profile: String?, // string Name of the profile to which the pump has been switched.
@SerializedName("reason") val reason: String?, // string For example the reason why the profile has been switched or why the temporary target has been set.
@SerializedName("notes") val notes: String?, // string Description/notes of treatment.
@SerializedName("enteredBy") val enteredBy: String?, // string Who entered the treatment.
@SerializedName("glucose") val glucose: Double? = null, // double Current glucose
@SerializedName("glucoseType") val glucoseType: String? = null, // string example: "Sensor", "Finger", "Manual"
@SerializedName("units") val units: String? = null, // string The units for the glucose value, mg/dl or mmol/l. It is strongly recommended to fill in this field.
@SerializedName("carbs") val carbs: Double? = null, // number... Amount of carbs given.
@SerializedName("protein") val protein: Int? = null, // number... Amount of protein given.
@SerializedName("fat") val fat: Int? = null, // number... Amount of fat given.
@SerializedName("insulin") val insulin: Double? = null, // number... Amount of insulin, if any.
@SerializedName("duration") val duration: Long? = null, // number... Duration in minutes.
@SerializedName("durationInMilliseconds") val durationInMilliseconds: Long? = null, // number... Duration in milliseconds.
@SerializedName("preBolus") val preBolus: Int? = null, // number... How many minutes the bolus was given before the meal started.
@SerializedName("splitNow") val splitNow: Int? = null, // number... Immediate part of combo bolus (in percent).
@SerializedName("splitExt") val splitExt: Int? = null, // number... Extended part of combo bolus (in percent).
@SerializedName("percent") val percent: Double? = null, // number... Eventual basal change in percent.
@SerializedName("absolute") val absolute: Double? = null, // number... Eventual basal change in absolute value (insulin units per hour).
@SerializedName("targetTop") val targetTop: Double? = null, // number... Top limit of temporary target.
@SerializedName("targetBottom") val targetBottom: Double? = null, // number... Bottom limit of temporary target.
@SerializedName("profile") val profile: String? = null, // string Name of the profile to which the pump has been switched.
@SerializedName("reason") val reason: String? = null, // string For example the reason why the profile has been switched or why the temporary target has been set.
@SerializedName("notes") val notes: String? = null, // string Description/notes of treatment.
@SerializedName("enteredBy") val enteredBy: String? = null, // string Who entered the treatment.
@SerializedName("endId") val endId: Long?, // long id of record which ended this
@SerializedName("pumpId") val pumpId: Long?, // long or "Meal Bolus", "Correction Bolus", "Combo Bolus" ex 4102 not sure if long or int
@SerializedName("pumpType") val pumpType: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" ex "ACCU_CHEK_INSIGHT_BLUETOOTH",
@SerializedName("pumpSerial") val pumpSerial: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" "33013206",
@SerializedName("endId") val endId: Long? = null, // long id of record which ended this
@SerializedName("pumpId") val pumpId: Long? = null, // long or "Meal Bolus", "Correction Bolus", "Combo Bolus" ex 4102 not sure if long or int
@SerializedName("pumpType") val pumpType: String? = null, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" ex "ACCU_CHEK_INSIGHT_BLUETOOTH",
@SerializedName("pumpSerial") val pumpSerial: String? = null, // string "Meal Bolus", "Correction Bolus", "Combo Bolus" "33013206",
// other fields found in examples but not in documentation
@SerializedName("profileJson") val profileJson: String?, // string "Profile Switch" ex json toString "{\"units\":\"mg\\/dl\",\"dia\":5,\"timezone\":\"Africa\\/Cairo\",
@SerializedName("profileJson") val profileJson: String? = null, // string "Profile Switch" ex json toString "{\"units\":\"mg\\/dl\",\"dia\":5,\"timezone\":\"Africa\\/Cairo\",
// \"sens\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":60},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":60},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":61.33333333333333},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":65.33333333333333},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":69.33333333333333},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":73.33333333333333},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":72},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":68},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":65.33333333333333},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":65.33333333333333}],\"carbratio\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":5.7333333333333325},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":7.333333333333333},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":6.666666666666666}],\"basal\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":0.5249999999999999},{\"time\":\"01:00\",\"timeAsSeconds\":3600,\"value\":0.585},{\"time\":\"02:00\",\"timeAsSeconds\":7200,\"value\":0.6375},{\"time\":\"03:00\",\"timeAsSeconds\":10800,\"value\":0.5625},{\"time\":\"04:00\",\"timeAsSeconds\":14400,\"value\":0.4575},{\"time\":\"05:00\",\"timeAsSeconds\":18000,\"value\":0.5175},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":0.48},{\"time\":\"07:00\",\"timeAsSeconds\":25200,\"value\":0.51},{\"time\":\"08:00\",\"timeAsSeconds\":28800,\"value\":0.48750000000000004},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":0.48},{\"time\":\"10:00\",\"timeAsSeconds\":36000,\"value\":0.48750000000000004},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":0.5025000000000001},{\"time\":\"12:00\",\"timeAsSeconds\":43200,\"value\":0.5549999999999999},{\"time\":\"13:00\",\"timeAsSeconds\":46800,\"value\":0.5700000000000001},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":0.5700000000000001},{\"time\":\"15:00\",\"timeAsSeconds\":54000,\"value\":0.5775},{\"time\":\"16:00\",\"timeAsSeconds\":57600,\"value\":0.51},{\"time\":\"17:00\",\"timeAsSeconds\":61200,\"value\":0.54},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":0.48750000000000004},{\"time\":\"19:00\",\"timeAsSeconds\":68400,\"value\":0.5249999999999999},{\"time\":\"20:00\",\"timeAsSeconds\":72000,\"value\":0.46499999999999997},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":0.46499999999999997},{\"time\":\"22:00\",\"timeAsSeconds\":79200,\"value\":0.43499999999999994},{\"time\":\"23:00\",\"timeAsSeconds\":82800,\"value\":0.41250000000000003}],\"target_low\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}],\"target_high\":[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":100},{\"time\":\"06:00\",\"timeAsSeconds\":21600,\"value\":90},{\"time\":\"09:00\",\"timeAsSeconds\":32400,\"value\":100},{\"time\":\"11:00\",\"timeAsSeconds\":39600,\"value\":90},{\"time\":\"14:00\",\"timeAsSeconds\":50400,\"value\":100},{\"time\":\"18:00\",\"timeAsSeconds\":64800,\"value\":90},{\"time\":\"21:00\",\"timeAsSeconds\":75600,\"value\":100}]}",
@SerializedName("originalProfileName") val originalProfileName: String?, // string "Effective Profile Switch"
@SerializedName("originalCustomizedName") val originalCustomizedName: String?, // string "Effective Profile Switch"
@SerializedName("originalTimeshift") val originalTimeshift: Long?, // long "Effective Profile Switch"
@SerializedName("originalPercentage") val originalPercentage: Int?, // int "Effective Profile Switch"
@SerializedName("originalDuration") val originalDuration: Long?, // long "Effective Profile Switch"
@SerializedName("originalEnd") val originalEnd: Long?, // long "Effective Profile Switch"
@SerializedName("originalProfileName") val originalProfileName: String? = null, // string "Effective Profile Switch"
@SerializedName("originalCustomizedName") val originalCustomizedName: String? = null, // string "Effective Profile Switch"
@SerializedName("originalTimeshift") val originalTimeshift: Long? = null, // long "Effective Profile Switch"
@SerializedName("originalPercentage") val originalPercentage: Int? = null, // int "Effective Profile Switch"
@SerializedName("originalDuration") val originalDuration: Long? = null, // long "Effective Profile Switch"
@SerializedName("originalEnd") val originalEnd: Long? = null, // long "Effective Profile Switch"
@SerializedName("bolusCalculatorResult") val bolusCalculatorResult: String?, // string "Bolus Wizard" json toString ex "bolusCalculatorResult": "{\"basalIOB\":-0.247,\"bolusIOB\":-1.837,\"carbs\":45.0,\"carbsInsulin\":9.0,\"cob\":0.0,\"cobInsulin\":0.0,\"dateCreated\":1626202788810,\"glucoseDifference\":44.0,\"glucoseInsulin\":0.8979591836734694,\"glucoseTrend\":5.5,\"glucoseValue\":134.0,\"ic\":5.0,\"id\":331,\"interfaceIDs_backing\":{\"nightscoutId\":\"60ede2a4c574da0004a3869d\"},\"isValid\":true,\"isf\":49.0,\"note\":\"\",\"otherCorrection\":0.0,\"percentageCorrection\":90,\"profileName\":\"Tuned 13/01 90%Lyum\",\"superbolusInsulin\":0.0,\"targetBGHigh\":90.0,\"targetBGLow\":90.0,\"timestamp\":1626202783325,\"totalInsulin\":7.34,\"trendInsulin\":0.336734693877551,\"utcOffset\":7200000,\"version\":1,\"wasBasalIOBUsed\":true,\"wasBolusIOBUsed\":true,\"wasCOBUsed\":true,\"wasGlucoseUsed\":true,\"wasSuperbolusUsed\":false,\"wasTempTargetUsed\":false,\"wasTrendUsed\":true,\"wereCarbsUsed\":false}",
@SerializedName("type") val type: String?, // string "Meal Bolus", "Correction Bolus", "Combo Bolus", "Temp Basal" type of bolus "NORMAL", "SMB", "FAKE_EXTENDED"
@SerializedName("isSMB") val isSMB: Boolean, // boolean "Meal Bolus", "Correction Bolus", "Combo Bolus"
@SerializedName("enteredinsulin") val enteredinsulin: Double?, // number... "Combo Bolus" insulin is missing only enteredinsulin field found
@SerializedName("relative") val relative: Double?, // number... "Combo Bolus", "extendedEmulated" (not in doc see below)
@SerializedName("isEmulatingTempBasal") val isEmulatingTempBasal: Boolean, // boolean "Combo Bolus", "extendedEmulated" (not in doc see below)
@SerializedName("isAnnouncement") val isAnnouncement: Boolean, // boolean "Announcement"
@SerializedName("rate") val rate: Double?, // Double "Temp Basal" absolute rate (could be calculated with percent and profile information...)
@SerializedName("extendedEmulated") val extendedEmulated: RemoteTreatment?, // Gson of emulated EB
@SerializedName("timeshift") val timeshift: Long, // integer "Profile Switch"
@SerializedName("percentage") val percentage: Int?, // integer "Profile Switch"
@SerializedName("bolusCalculatorResult") val bolusCalculatorResult: String? = null, // string "Bolus Wizard" json toString ex "bolusCalculatorResult": "{\"basalIOB\":-0.247,\"bolusIOB\":-1.837,\"carbs\":45.0,\"carbsInsulin\":9.0,\"cob\":0.0,\"cobInsulin\":0.0,\"dateCreated\":1626202788810,\"glucoseDifference\":44.0,\"glucoseInsulin\":0.8979591836734694,\"glucoseTrend\":5.5,\"glucoseValue\":134.0,\"ic\":5.0,\"id\":331,\"interfaceIDs_backing\":{\"nightscoutId\":\"60ede2a4c574da0004a3869d\"},\"isValid\":true,\"isf\":49.0,\"note\":\"\",\"otherCorrection\":0.0,\"percentageCorrection\":90,\"profileName\":\"Tuned 13/01 90%Lyum\",\"superbolusInsulin\":0.0,\"targetBGHigh\":90.0,\"targetBGLow\":90.0,\"timestamp\":1626202783325,\"totalInsulin\":7.34,\"trendInsulin\":0.336734693877551,\"utcOffset\":7200000,\"version\":1,\"wasBasalIOBUsed\":true,\"wasBolusIOBUsed\":true,\"wasCOBUsed\":true,\"wasGlucoseUsed\":true,\"wasSuperbolusUsed\":false,\"wasTempTargetUsed\":false,\"wasTrendUsed\":true,\"wereCarbsUsed\":false}",
@SerializedName("type") val type: String? = null, // string "Meal Bolus", "Correction Bolus", "Combo Bolus", "Temp Basal" type of bolus "NORMAL", "SMB", "FAKE_EXTENDED"
@SerializedName("isSMB") val isSMB: Boolean? = null, // boolean "Meal Bolus", "Correction Bolus", "Combo Bolus"
@SerializedName("enteredinsulin") val enteredinsulin: Double? = null, // number... "Combo Bolus" insulin is missing only enteredinsulin field found
@SerializedName("relative") val relative: Double? = null, // number... "Combo Bolus", "extendedEmulated" (not in doc see below)
@SerializedName("isEmulatingTempBasal") val isEmulatingTempBasal: Boolean? = null, // boolean "Combo Bolus", "extendedEmulated" (not in doc see below)
@SerializedName("isAnnouncement") val isAnnouncement: Boolean? = null, // boolean "Announcement"
@SerializedName("rate") val rate: Double? = null, // Double "Temp Basal" absolute rate (could be calculated with percent and profile information...)
@SerializedName("extendedEmulated") val extendedEmulated: RemoteTreatment? = null, // Gson of emulated EB
@SerializedName("timeshift") val timeshift: Long? = null, // integer "Profile Switch"
@SerializedName("percentage") val percentage: Int? = null // integer "Profile Switch"
) {
fun timestamp(): Long {
return date ?: mills ?: timestamp ?: fromISODateString(created_at)
return date ?: mills ?: timestamp ?: created_at?. let { fromISODateString(created_at) } ?: 0L
}
private fun fromISODateString(isoDateString: String): Long =

View file

@ -5,6 +5,7 @@ import info.nightscout.core.extensions.getCustomizedName
import info.nightscout.core.extensions.pureProfileFromJson
import info.nightscout.core.profile.ProfileSealed
import info.nightscout.database.entities.ProfileSwitch
import info.nightscout.database.entities.TherapyEvent
import info.nightscout.database.entities.embedments.InterfaceIDs
import info.nightscout.interfaces.plugin.ActivePlugin
import info.nightscout.interfaces.utils.JsonHelper
@ -23,7 +24,7 @@ fun ProfileSwitch.toJson(isAdd: Boolean, dateUtil: DateUtil): JSONObject =
.put("created_at", dateUtil.toISOString(timestamp))
.put("enteredBy", "openaps://" + "AndroidAPS")
.put("isValid", isValid)
.put("eventType", info.nightscout.database.entities.TherapyEvent.Type.PROFILE_SWITCH.text)
.put("eventType", TherapyEvent.Type.PROFILE_SWITCH.text)
.also { // remove customization to store original profileJson in toPureNsJson call
timeshift = 0
percentage = 100

View file

@ -11,6 +11,6 @@ fun NSExtendedBolus.toExtendedBolus(): ExtendedBolus =
utcOffset = utcOffset,
amount = enteredinsulin,
duration = duration,
isEmulatingTempBasal = isEmulatingTempbasal,
isEmulatingTempBasal = isEmulatingTempBasal ?: false,
interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId)
)