diff --git a/app/src/main/kotlin/app/aaps/di/PluginsListModule.kt b/app/src/main/kotlin/app/aaps/di/PluginsListModule.kt
index 4ad2be2d8ac..b708e1365cd 100644
--- a/app/src/main/kotlin/app/aaps/di/PluginsListModule.kt
+++ b/app/src/main/kotlin/app/aaps/di/PluginsListModule.kt
@@ -35,6 +35,7 @@ import app.aaps.plugins.smoothing.AvgSmoothingPlugin
import app.aaps.plugins.smoothing.ExponentialSmoothingPlugin
import app.aaps.plugins.smoothing.NoSmoothingPlugin
import app.aaps.plugins.source.DexcomPlugin
+import app.aaps.plugins.source.EversensePlugin
import app.aaps.plugins.source.GlimpPlugin
import app.aaps.plugins.source.GlunovoPlugin
import app.aaps.plugins.source.IntelligoPlugin
@@ -390,7 +391,13 @@ abstract class PluginsListModule {
@IntoMap
@IntKey(410)
abstract fun bindNSClientSourcePlugin(plugin: NSClientSourcePlugin): PluginBase
-
+
+ @Binds
+ @AllConfigs
+ @IntoMap
+ @IntKey(415)
+ abstract fun bindEversenseSourcePlugin(plugin: EversensePlugin): PluginBase
+
@Binds
@AllConfigs
@IntoMap
diff --git a/core/data/src/main/kotlin/app/aaps/core/data/model/SourceSensor.kt b/core/data/src/main/kotlin/app/aaps/core/data/model/SourceSensor.kt
index fe09dec7adb..d47fa2e9df5 100644
--- a/core/data/src/main/kotlin/app/aaps/core/data/model/SourceSensor.kt
+++ b/core/data/src/main/kotlin/app/aaps/core/data/model/SourceSensor.kt
@@ -29,7 +29,8 @@ enum class SourceSensor(val text: String) {
OTTAI("Ottai"),
SIBIONIC("SI App"),
SINO("Sino App"),
- EVERSENSE("Eversense"),
+ EVERSENSE_E3("Eversense E3"),
+ EVERSENSE_365("Eversense 365"),
AIDEX("GlucoRx Aidex"),
SYAI_TAG("Syai Tag"),
RANDOM("Random"),
diff --git a/database/impl/src/main/kotlin/app/aaps/database/entities/GlucoseValue.kt b/database/impl/src/main/kotlin/app/aaps/database/entities/GlucoseValue.kt
index fb8614d4a80..156b0e1e27c 100644
--- a/database/impl/src/main/kotlin/app/aaps/database/entities/GlucoseValue.kt
+++ b/database/impl/src/main/kotlin/app/aaps/database/entities/GlucoseValue.kt
@@ -93,7 +93,8 @@ data class GlucoseValue(
GLUNOVO_NATIVE,
INTELLIGO_NATIVE,
MM_600_SERIES,
- EVERSENSE,
+ EVERSENSE_E3,
+ EVERSENSE_365,
AIDEX,
RANDOM,
UNKNOWN,
diff --git a/database/persistence/src/main/kotlin/app/aaps/database/persistence/converters/SourceSensorExtension.kt b/database/persistence/src/main/kotlin/app/aaps/database/persistence/converters/SourceSensorExtension.kt
index 59065baec9f..0c369acc57b 100644
--- a/database/persistence/src/main/kotlin/app/aaps/database/persistence/converters/SourceSensorExtension.kt
+++ b/database/persistence/src/main/kotlin/app/aaps/database/persistence/converters/SourceSensorExtension.kt
@@ -30,7 +30,8 @@ fun GlucoseValue.SourceSensor.fromDb(): SourceSensor =
GlucoseValue.SourceSensor.GLUNOVO_NATIVE -> SourceSensor.GLUNOVO_NATIVE
GlucoseValue.SourceSensor.INTELLIGO_NATIVE -> SourceSensor.INTELLIGO_NATIVE
GlucoseValue.SourceSensor.MM_600_SERIES -> SourceSensor.MM_600_SERIES
- GlucoseValue.SourceSensor.EVERSENSE -> SourceSensor.EVERSENSE
+ GlucoseValue.SourceSensor.EVERSENSE_E3 -> SourceSensor.EVERSENSE_E3
+ GlucoseValue.SourceSensor.EVERSENSE_365 -> SourceSensor.EVERSENSE_365
GlucoseValue.SourceSensor.AIDEX -> SourceSensor.AIDEX
GlucoseValue.SourceSensor.RANDOM -> SourceSensor.RANDOM
GlucoseValue.SourceSensor.UNKNOWN -> SourceSensor.UNKNOWN
@@ -73,7 +74,8 @@ fun SourceSensor.toDb(): GlucoseValue.SourceSensor =
SourceSensor.GLUNOVO_NATIVE -> GlucoseValue.SourceSensor.GLUNOVO_NATIVE
SourceSensor.INTELLIGO_NATIVE -> GlucoseValue.SourceSensor.INTELLIGO_NATIVE
SourceSensor.MM_600_SERIES -> GlucoseValue.SourceSensor.MM_600_SERIES
- SourceSensor.EVERSENSE -> GlucoseValue.SourceSensor.EVERSENSE
+ SourceSensor.EVERSENSE_E3 -> GlucoseValue.SourceSensor.EVERSENSE_E3
+ SourceSensor.EVERSENSE_365 -> GlucoseValue.SourceSensor.EVERSENSE_365
SourceSensor.AIDEX -> GlucoseValue.SourceSensor.AIDEX
SourceSensor.RANDOM -> GlucoseValue.SourceSensor.RANDOM
SourceSensor.UNKNOWN -> GlucoseValue.SourceSensor.UNKNOWN
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7a7711270c6..166ab08c795 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -75,7 +75,7 @@ kotlin-stdlib-jdk8 = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk
kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" }
kotlinx-serialization-bom = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-bom", version.ref = "kotlinx-serialization" }
-kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json" }
+kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
kotlinx-serialization-protobuf = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-protobuf" }
kotlinx-serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core" }
diff --git a/plugins/eversense/.gitignore b/plugins/eversense/.gitignore
new file mode 100644
index 00000000000..42afabfd2ab
--- /dev/null
+++ b/plugins/eversense/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/plugins/eversense/build.gradle.kts b/plugins/eversense/build.gradle.kts
new file mode 100644
index 00000000000..1c1f990baed
--- /dev/null
+++ b/plugins/eversense/build.gradle.kts
@@ -0,0 +1,20 @@
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.ksp)
+
+ id("kotlin-android")
+ id("kotlinx-serialization")
+ id("android-module-dependencies")
+}
+
+android {
+ namespace = "com.nightscout.eversense"
+}
+
+dependencies {
+ api(libs.androidx.core)
+ api(libs.kotlinx.serialization.json)
+
+ api(libs.org.slf4j.api)
+ api(libs.com.github.tony19.logback.android)
+}
\ No newline at end of file
diff --git a/plugins/eversense/consumer-rules.pro b/plugins/eversense/consumer-rules.pro
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/plugins/eversense/proguard-rules.pro b/plugins/eversense/proguard-rules.pro
new file mode 100644
index 00000000000..481bb434814
--- /dev/null
+++ b/plugins/eversense/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/plugins/eversense/src/main/AndroidManifest.xml b/plugins/eversense/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..9189b499de4
--- /dev/null
+++ b/plugins/eversense/src/main/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseCGMPlugin.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseCGMPlugin.kt
new file mode 100644
index 00000000000..363965ed9ea
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseCGMPlugin.kt
@@ -0,0 +1,143 @@
+package com.nightscout.eversense
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothManager
+import android.bluetooth.le.ScanFilter
+import android.bluetooth.le.ScanSettings
+import android.content.Context
+import android.content.SharedPreferences
+import android.os.ParcelUuid
+import com.nightscout.eversense.callbacks.EversenseScanCallback
+import com.nightscout.eversense.callbacks.EversenseWatcher
+import com.nightscout.eversense.models.EversenseState
+import com.nightscout.eversense.models.EversenseTransmitterSettings
+import com.nightscout.eversense.packets.EversenseE3Communicator
+import kotlinx.serialization.json.Json
+
+class EversenseCGMPlugin {
+ private var context: Context? = null
+
+ private var bluetoothManager: BluetoothManager? = null
+ private var preferences: SharedPreferences? = null
+ private var gattCallback: EversenseGattCallback? = null
+
+ private var scanner: EversenseScanner? = null
+ var watchers: List = listOf()
+
+ fun setContext(context: Context, loggingEnabled: Boolean) {
+ this.context = context
+ EversenseLogger.instance.enableLogging(loggingEnabled)
+
+ val preference = context.getSharedPreferences(TAG, Context.MODE_PRIVATE)
+
+ bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
+ preferences = preference
+ gattCallback = EversenseGattCallback(this, preference)
+ }
+
+ fun addWatcher(watcher: EversenseWatcher) {
+ this.watchers += watcher
+ }
+
+ fun isConnected(): Boolean {
+ val gattCallback = this.gattCallback ?:run {
+ return false
+ }
+
+ return gattCallback.isConnected()
+ }
+
+ fun getCurrentState(): EversenseState? {
+ val preferences = preferences ?:run {
+ EversenseLogger.error(TAG, "No preferences available. Make sure setContext has been called")
+ return null
+ }
+
+ val stateJson = preferences.getString(StorageKeys.STATE, null) ?: "{}"
+ return Json.decodeFromString(stateJson)
+ }
+
+ @SuppressLint("MissingPermission")
+ fun startScan(callback: EversenseScanCallback) {
+ val bluetoothScanner = this.bluetoothManager?.adapter?.bluetoothLeScanner ?:run {
+ EversenseLogger.error(TAG, "No bluetooth manager available. Make sure setContext has been called")
+ return
+ }
+
+ scanner = EversenseScanner(callback)
+ val filters = listOf(
+ ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(EversenseGattCallback.serviceUUID)).build()
+ )
+ val settings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build()
+ bluetoothScanner.startScan(filters, settings, scanner)
+ }
+
+ @SuppressLint("MissingPermission")
+ fun connect(device: BluetoothDevice?): Boolean {
+ val bluetoothManager = this.bluetoothManager ?:run {
+ EversenseLogger.error(TAG, "No bluetooth manager available. Make sure setContext has been called")
+ return false
+ }
+
+ val gattCallback = this.gattCallback ?:run {
+ EversenseLogger.error(TAG, "No gattCallback available. Make sure setContext has been called")
+ return false
+ }
+
+ if (scanner != null) {
+ bluetoothManager.adapter.bluetoothLeScanner.stopScan(scanner)
+ }
+
+ if (gattCallback.isConnected()) {
+ EversenseLogger.info(TAG, "Already connected!")
+ return true
+ }
+
+ if (device != null) {
+ EversenseLogger.info(TAG, "Connecting to ${device.name}")
+ device.connectGatt(context, true, gattCallback)
+ return true
+ }
+
+ val address = preferences?.getString(StorageKeys.REMOTE_DEVICE_KEY, null) ?:run {
+ EversenseLogger.error(TAG, "Remote device not stored. Make sure you've connected once and bonded to this device")
+ return false
+ }
+
+ val remoteDevice = bluetoothManager.adapter.getRemoteDevice(address) ?:run {
+ EversenseLogger.error(TAG, "Remote device not found. Make sure you've connected once and bonded to this device")
+ return false
+ }
+
+ remoteDevice.connectGatt(context, true, gattCallback)
+ return true
+ }
+
+ fun writeSettings(settings: EversenseTransmitterSettings): Boolean {
+ val preferences = preferences ?:run {
+ EversenseLogger.error(TAG, "No preferences available. Make sure setContext has been called")
+ return false
+ }
+
+ val gattCallback = this.gattCallback ?:run {
+ EversenseLogger.error(TAG, "No gattCallback available. Make sure transmitter is connected before writing settings")
+ return false
+ }
+
+ if (!gattCallback.isConnected()) {
+ EversenseLogger.error(TAG, "Transmitter is not connected...")
+ return false
+ }
+
+ return EversenseE3Communicator.writeSettings(gattCallback, preferences, settings)
+ }
+
+ companion object {
+ private const val TAG = "EversenseCGMManager"
+
+ val instance:EversenseCGMPlugin by lazy {
+ EversenseCGMPlugin()
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseGattCallback.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseGattCallback.kt
new file mode 100644
index 00000000000..bd63f3be4be
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseGattCallback.kt
@@ -0,0 +1,290 @@
+package com.nightscout.eversense
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothGatt
+import android.bluetooth.BluetoothGattCallback
+import android.bluetooth.BluetoothGattCharacteristic
+import android.bluetooth.BluetoothGattDescriptor
+import android.bluetooth.BluetoothGattService
+import android.bluetooth.BluetoothProfile
+import android.content.SharedPreferences
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.exceptions.EversenseWriteException
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversenseE3Communicator
+import com.nightscout.eversense.packets.e3.EversenseE3Packets
+import com.nightscout.eversense.packets.e3.SaveBondingInformationPacket
+import java.util.UUID
+import java.util.concurrent.Executors
+import kotlin.jvm.Throws
+import androidx.core.content.edit
+
+class EversenseGattCallback(
+ private val plugin: EversenseCGMPlugin,
+ private val preferences: SharedPreferences
+) : BluetoothGattCallback() {
+
+ companion object {
+ private val TAG = "EversenseGattCallback"
+
+ const val serviceUUID = "c3230001-9308-47ae-ac12-3d030892a211"
+
+ private const val requestUUID = "6eb0f021-a7ba-7e7d-66c9-6d813f01d273"
+ private const val requestSecureV2UUID = "c3230002-9308-47ae-ac12-3d030892a211"
+
+ private const val responseUUID = "6eb0f024-bd60-7aaa-25a7-0029573f4f23"
+ private const val responseSecureV2UUID = "c3230003-9308-47ae-ac12-3d030892a211"
+ private const val magicDescriptorUUID = "00002902-0000-1000-8000-00805f9b34fb"
+ }
+
+ private val executor = Executors.newSingleThreadExecutor()
+ private var bluetoothGatt: BluetoothGatt? = null
+ private var eversenseBluetoothService: BluetoothGattService? = null
+ private var requestCharacteristic: BluetoothGattCharacteristic? = null
+ private var responseCharacteristic: BluetoothGattCharacteristic? = null
+
+ private var payloadSize: Int = 20
+ private var security: EversenseSecurityType = EversenseSecurityType.None
+ var currentPacket: EversenseBasePacket? = null
+
+ fun isConnected(): Boolean {
+ return this.bluetoothGatt != null
+ }
+
+ @SuppressLint("MissingPermission")
+ override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
+ EversenseLogger.info(TAG, "Connection state changed - state: $status, newState: $newState")
+
+ if (status == BluetoothGatt.GATT_SUCCESS && newState == BluetoothProfile.STATE_CONNECTED) {
+ bluetoothGatt = gatt
+
+ preferences.edit(commit = true) {
+ putString(StorageKeys.REMOTE_DEVICE_KEY, gatt.device.address)
+ }
+
+ for (watcher in plugin.watchers) {
+ watcher.onConnectionChanged(true)
+ }
+
+ if (!gatt.requestMtu(512)) {
+ EversenseLogger.error(TAG, "Failed to request MTU")
+ }
+ return
+ }
+
+ if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+ EversenseLogger.warning(TAG, "Disconnected...")
+ bluetoothGatt = null
+
+ for (watcher in plugin.watchers) {
+ watcher.onConnectionChanged(false)
+ }
+ }
+ }
+
+ @SuppressLint("MissingPermission")
+ override fun onMtuChanged(gatt: BluetoothGatt?, mtu: Int, status: Int) {
+ if (status == 0) {
+ payloadSize = mtu - 3
+ EversenseLogger.debug(TAG, "New payload size: $payloadSize")
+ } else {
+ payloadSize = 20
+ EversenseLogger.error(TAG, "Failed to set payload size - status: $status")
+ }
+
+ val success = gatt?.discoverServices()
+ EversenseLogger.info(TAG, "Trigger discover services - success: $success")
+ }
+
+ @SuppressLint("MissingPermission")
+ override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
+ EversenseLogger.info(TAG, "Discovered services - status: $status")
+
+ if (gatt == null) {
+ EversenseLogger.error(TAG, "Gatt is empty")
+ return
+ }
+
+ val service = gatt.services.first { it.uuid.toString() == serviceUUID }
+ if (service == null) {
+ EversenseLogger.error(TAG, "Service is empty -> disconnecting from device")
+ gatt.disconnect()
+ return
+ }
+
+ eversenseBluetoothService = service
+ if (service.characteristics.isEmpty()) {
+ EversenseLogger.error(TAG, "Service has no characteristics -> disconnecting from device")
+ gatt.disconnect()
+ return
+ }
+
+ var requestChar = service.characteristics.find { it.uuid.toString() == requestUUID }
+ var responseChar = service.characteristics.find { it.uuid.toString() == responseUUID }
+ if (requestChar != null && responseChar != null) {
+ EversenseLogger.info(TAG, "Connected to Eversense E3!")
+ security = EversenseSecurityType.None
+ requestCharacteristic = requestChar
+ responseCharacteristic = responseChar
+
+ gatt.setCharacteristicNotification(requestChar, true)
+ gatt.setCharacteristicNotification(responseChar, true)
+
+ enableNotify(gatt, responseChar)
+ return
+ }
+
+ requestChar = service.characteristics.find { it.uuid.toString() == requestSecureV2UUID }
+ responseChar = service.characteristics.find { it.uuid.toString() == responseSecureV2UUID }
+ if (requestChar == null || responseChar == null) {
+ EversenseLogger.error(TAG, "No Eversense request/response characteristic found -> Disconnect from device...")
+ gatt.disconnect()
+ return
+ }
+
+ EversenseLogger.info(TAG, "Connected to Eversense 365!")
+ security = EversenseSecurityType.SecureV2
+ requestCharacteristic = requestChar
+ responseCharacteristic = responseChar
+
+ gatt.setCharacteristicNotification(requestChar, true)
+ gatt.setCharacteristicNotification(responseChar, true)
+ enableNotify(gatt, responseChar)
+ }
+
+ override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
+ EversenseLogger.debug(TAG, "onDescriptor (${descriptor.uuid}) write for characteristic (${descriptor.characteristic.uuid}) - status $status")
+
+ if (status == BluetoothGatt.GATT_SUCCESS && descriptor.uuid.toString() == magicDescriptorUUID) {
+ if (descriptor.characteristic.uuid.toString() == responseUUID) {
+ executor.submit { authE3flow() }
+ } else if (descriptor.characteristic.uuid.toString() == responseSecureV2UUID) {
+ executor.submit { authV2flow() }
+ }
+ }
+ }
+
+ @OptIn(ExperimentalStdlibApi::class)
+ override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
+ EversenseLogger.debug(TAG, "Received data: ${characteristic.value.toHexString()}")
+
+ var data = characteristic.value
+ if (security == EversenseSecurityType.SecureV2) {
+ data = data.drop(3).toByteArray()
+ }
+
+ if (EversenseE3Packets.isPushPacket(data[0])) {
+ EversenseLogger.debug(TAG, "Keep Alive packet received!")
+ executor.submit {
+ EversenseE3Communicator.readGlucose(this, preferences, plugin.watchers)
+ EversenseE3Communicator.fullSync(this, preferences, plugin.watchers)
+ }
+ return
+ }
+
+ val packet = currentPacket ?:run {
+ EversenseLogger.warning(TAG, "currentPacket is empty -> Skip packet...")
+ return
+ }
+
+ synchronized(packet) {
+ val packetAnnotation = packet.getAnnotation() ?:run {
+ EversenseLogger.warning(TAG, "annotation is empty -> Skip packet...")
+ return
+ }
+
+ if (EversenseE3Packets.isErrorPacket(data[0])) {
+ EversenseLogger.error(TAG, "Received error response - data: ${data.toHexString()}")
+ packet.notifyAll()
+ return
+ }
+
+ if (packetAnnotation.responseId != data[0]) {
+ EversenseLogger.warning(TAG, "Incorrect responseId received - Expected: ${packetAnnotation.responseId}, got: ${data[0]}")
+ return
+ }
+
+ if (security == EversenseSecurityType.None) {
+ packet.appendData(data.toUByteArray())
+ packet.notifyAll()
+ } else {
+ if (packetAnnotation.responseType != data[1]) {
+ EversenseLogger.warning(TAG, "Incorrect responseType received - Expected: ${packetAnnotation.responseType}, got: ${data[1]}")
+ return
+ }
+
+ packet.appendData(data.toUByteArray())
+ packet.notifyAll()
+ }
+ }
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ @SuppressLint("MissingPermission")
+ @OptIn(ExperimentalStdlibApi::class)
+ @Throws(EversenseWriteException::class)
+ funwritePacket(packet: EversenseBasePacket): T {
+ val gatt = bluetoothGatt ?:run {
+ throw EversenseWriteException("Gatt is empty")
+ }
+
+ val requestCharacteristic = requestCharacteristic ?:run {
+ throw EversenseWriteException("requestCharacteristic is empty")
+ }
+
+ val requestData = packet.buildRequest() ?:run {
+ throw EversenseWriteException("Failed to build request data...")
+ }
+
+ currentPacket = packet
+
+ EversenseLogger.debug(TAG, "Writing data: ${requestData.toHexString()}")
+
+ requestCharacteristic.setValue(requestData)
+ gatt.writeCharacteristic(requestCharacteristic)
+
+ synchronized(packet) {
+ try {
+ packet.wait(5000)
+ } catch (e: Exception) {
+ EversenseLogger.error(TAG, "Exception during await - exception: $e")
+ e.printStackTrace()
+ }
+ }
+
+ return try {
+ val response = packet.parseResponse()
+ currentPacket = null
+ response as? T ?: throw EversenseWriteException("Unable to cast response")
+ } catch(e: Exception) {
+ throw EversenseWriteException("Failed to parse response - exception: $e")
+ }
+ }
+
+ private fun authE3flow() {
+ EversenseLogger.info(TAG, "Starting auth flow E3...")
+
+ try {
+ writePacket(SaveBondingInformationPacket())
+ } catch (exception: Exception) {
+ EversenseLogger.error(TAG, "Failed to do auth flow - exception: $exception")
+ return
+ }
+
+ EversenseLogger.info(TAG, "Ready for full sync!!")
+ EversenseE3Communicator.fullSync(this, preferences, plugin.watchers)
+ }
+
+ private fun authV2flow() {
+ // TODO: Implement
+ }
+
+ @SuppressLint("MissingPermission")
+ @Suppress("DEPRECATION")
+ private fun enableNotify(gatt: BluetoothGatt, responseCharacteristic: BluetoothGattCharacteristic) {
+ responseCharacteristic.getDescriptor(UUID.fromString(magicDescriptorUUID))?.let {
+ it.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
+ gatt.writeDescriptor(it)
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseLogger.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseLogger.kt
new file mode 100644
index 00000000000..3020c8dc733
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseLogger.kt
@@ -0,0 +1,119 @@
+package com.nightscout.eversense
+
+import ch.qos.logback.classic.LoggerContext
+import ch.qos.logback.classic.joran.JoranConfigurator
+import ch.qos.logback.core.joran.spi.JoranException
+import org.slf4j.LoggerFactory
+import java.io.ByteArrayInputStream
+import java.io.InputStream
+
+class EversenseLogger {
+ private val lc = LoggerContext()
+ private var isEnabled: Boolean = true
+
+ init {
+ val config = JoranConfigurator()
+ config.setContext(lc)
+
+ val stream: InputStream = ByteArrayInputStream(LOGBACK_XML.toByteArray())
+ try {
+ config.doConfigure(stream)
+ } catch (e: JoranException) {
+ e.printStackTrace()
+ }
+ }
+
+ private fun debug(tag: String, message: String) {
+ if (!isEnabled) { return }
+
+ lc.getLogger(tag).info(logLocationPrefix() + message)
+ }
+
+ private fun info(tag: String, message: String) {
+ if (!isEnabled) { return }
+ lc.getLogger(tag).info(logLocationPrefix() + message)
+ }
+
+ private fun warning(tag: String, message: String) {
+ if (!isEnabled) { return }
+ lc.getLogger(tag).warn(logLocationPrefix() + message)
+ }
+
+ private fun error(tag: String, message: String) {
+ if (!isEnabled) { return }
+ lc.getLogger(tag).error(logLocationPrefix() + message)
+ }
+
+ fun enableLogging(value: Boolean) {
+ this.isEnabled = value
+ }
+
+ private fun logLocationPrefix(): String {
+ val stackInfo = Throwable().stackTrace[4]
+ val className = stackInfo.className.substringAfterLast(".")
+ val methodName = stackInfo.methodName
+ val lineNumber = stackInfo.lineNumber
+
+ return "$className.$methodName():$lineNumber]: "
+ }
+
+ companion object {
+ val instance = EversenseLogger()
+
+ fun debug(tag: String, message: String) {
+ instance.debug(tag, message)
+ }
+
+ fun info(tag: String, message: String) {
+ instance.info(tag, message)
+ }
+
+ fun warning(tag: String, message: String) {
+ instance.warning(tag, message)
+ }
+
+ fun error(tag: String, message: String) {
+ instance.error(tag, message)
+ }
+
+ private const val LOGBACK_XML: String = "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \${EXT_FILES_DIR}/Eversense.log\n" +
+ " \n" +
+ " \n" +
+ " \${EXT_FILES_DIR}/Eversense._%d{yyyy-MM-dd}_%d{HH-mm-ss, aux}_.%i.zip\n" +
+ " \n" +
+ "\n" +
+ " \n" +
+ " 5MB\n" +
+ " \n" +
+ " \n" +
+ " 120\n" +
+ " \n" +
+ " \n" +
+ " [%d{HH:mm:ss.SSS} %.-1level/%logger %msg%n\n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " %logger{0}\n" +
+ " \n" +
+ " \n" +
+ " [%d{HH:mm:ss.SSS} %msg%n\n" +
+ " \n" +
+ " \n" +
+ "\n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ ""
+
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseScanner.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseScanner.kt
new file mode 100644
index 00000000000..837e42ee605
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/EversenseScanner.kt
@@ -0,0 +1,24 @@
+package com.nightscout.eversense
+
+import android.annotation.SuppressLint
+import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanResult
+import android.util.Log
+import com.nightscout.eversense.callbacks.EversenseScanCallback
+import com.nightscout.eversense.models.EversenseScanResult
+
+class EversenseScanner(private val callback: EversenseScanCallback): ScanCallback() {
+ @SuppressLint("MissingPermission")
+ override fun onScanResult(callbackType: Int, scanRecord: ScanResult) {
+ if (scanRecord.device?.name == null) {
+ return
+ }
+
+ EversenseLogger.info(TAG, "Found device: ${scanRecord.device.name}")
+ callback.onResult(EversenseScanResult(scanRecord.device.name, scanRecord.rssi, scanRecord.device))
+ }
+
+ companion object {
+ private val TAG = "EversenseScanner"
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/StorageKeys.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/StorageKeys.kt
new file mode 100644
index 00000000000..ac3fe1823c4
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/StorageKeys.kt
@@ -0,0 +1,8 @@
+package com.nightscout.eversense
+
+class StorageKeys {
+ companion object {
+ const val REMOTE_DEVICE_KEY = "remote_device"
+ const val STATE = "eversense_state"
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/callbacks/EversenseScanCallback.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/callbacks/EversenseScanCallback.kt
new file mode 100644
index 00000000000..201c80d865e
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/callbacks/EversenseScanCallback.kt
@@ -0,0 +1,7 @@
+package com.nightscout.eversense.callbacks
+
+import com.nightscout.eversense.models.EversenseScanResult
+
+interface EversenseScanCallback {
+ fun onResult(var0: EversenseScanResult)
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/callbacks/EversenseWatcher.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/callbacks/EversenseWatcher.kt
new file mode 100644
index 00000000000..2d6168df913
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/callbacks/EversenseWatcher.kt
@@ -0,0 +1,11 @@
+package com.nightscout.eversense.callbacks
+
+import com.nightscout.eversense.enums.EversenseType
+import com.nightscout.eversense.models.EversenseCGMResult
+import com.nightscout.eversense.models.EversenseState
+
+interface EversenseWatcher {
+ fun onCGMRead(type: EversenseType, readings: List)
+ fun onStateChanged(state: EversenseState)
+ fun onConnectionChanged(connected: Boolean)
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseE3Memory.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseE3Memory.kt
new file mode 100644
index 00000000000..c438bf909cb
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseE3Memory.kt
@@ -0,0 +1,31 @@
+package com.nightscout.eversense.enums
+
+enum class EversenseE3Memory(private val address: Long) {
+ BatteryPercentage(0x0000_0406),
+ SensorInsertionDate(0x0000_0890),
+ SensorInsertionTime(0x0000_0892),
+ VibrateMode(0x0000_0902),
+ HighGlucoseAlarmEnabled(0x0000_1029),
+ HighGlucoseAlarmThreshold(0x0000_110C),
+ LowGlucoseAlarmThreshold(0x0000_110A),
+ PredictiveAlert(0x0000_1020),
+ PredictiveLowTime(0x0000_1021),
+ PredictiveHighTime(0x0000_1022),
+ PredictiveLowAlert(0x0000_1027),
+ PredictiveHighAlert(0x0000_1028),
+ PredictiveLowTarget(0x0000_1102),
+ PredictiveHighTarget(0x0000_1104),
+ RateAlert(0x0000_1010),
+ RateFallingAlert(0x0000_1025),
+ RateRisingAlert(0x0000_1026),
+ RateFallingThreshold(0x0000_1011),
+ RateRisingThreshold(0x0000_1012);
+
+ fun getRequestData(): ByteArray {
+ return byteArrayOf(
+ this.address.toByte(),
+ (this.address shr 8).toByte(),
+ (this.address shr 16).toByte(),
+ )
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseSecurityType.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseSecurityType.kt
new file mode 100644
index 00000000000..8eee37eab41
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseSecurityType.kt
@@ -0,0 +1,10 @@
+package com.nightscout.eversense.enums
+
+enum class EversenseSecurityType {
+ // Eversense E3
+ None,
+
+ // Eversense 365 -> generation 2
+ // Eversense 365 -> generation 1 is deprecated and not available anymore
+ SecureV2
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseTrendArrow.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseTrendArrow.kt
new file mode 100644
index 00000000000..dff7b445be8
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseTrendArrow.kt
@@ -0,0 +1,10 @@
+package com.nightscout.eversense.enums
+
+enum class EversenseTrendArrow(val type: String) {
+ NONE("NONE"),
+ SINGLE_UP("SingleUp"),
+ FORTY_FIVE_UP("FortyFiveUp"),
+ FLAT("Flat"),
+ FORTY_FIVE_DOWN("FortyFiveDown"),
+ SINGLE_DOWN("SingleDown")
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseType.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseType.kt
new file mode 100644
index 00000000000..104f452afd1
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/enums/EversenseType.kt
@@ -0,0 +1,6 @@
+package com.nightscout.eversense.enums
+
+enum class EversenseType {
+ EVERSENSE_E3,
+ EVERSENSE_365
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/exceptions/EversenseWriteException.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/exceptions/EversenseWriteException.kt
new file mode 100644
index 00000000000..604b3cbd50b
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/exceptions/EversenseWriteException.kt
@@ -0,0 +1,4 @@
+package com.nightscout.eversense.exceptions
+
+class EversenseWriteException(override val message: String) : Exception(message) {
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseCGMResult.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseCGMResult.kt
new file mode 100644
index 00000000000..c364e55b18e
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseCGMResult.kt
@@ -0,0 +1,9 @@
+package com.nightscout.eversense.models
+
+import com.nightscout.eversense.enums.EversenseTrendArrow
+
+data class EversenseCGMResult(
+ val glucoseInMgDl: Int,
+ val datetime: Long,
+ val trend: EversenseTrendArrow
+)
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseScanResult.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseScanResult.kt
new file mode 100644
index 00000000000..b3a9bcffef7
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseScanResult.kt
@@ -0,0 +1,5 @@
+package com.nightscout.eversense.models
+
+import android.bluetooth.BluetoothDevice
+
+data class EversenseScanResult(val name: String, val rssi: Int, val device: BluetoothDevice)
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseState.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseState.kt
new file mode 100644
index 00000000000..c9c295eaf60
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/models/EversenseState.kt
@@ -0,0 +1,39 @@
+package com.nightscout.eversense.models
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+class EversenseState {
+ var lastSync: Long = 0
+ var insertionDate: Long = 0
+
+ var batteryPercentage: Int = 0
+
+ var recentGlucoseDatetime: Long = 0
+ var recentGlucoseValue: Int = 0
+
+ var settings = EversenseTransmitterSettings()
+}
+
+@Serializable
+class EversenseTransmitterSettings {
+ var vibrateEnabled: Boolean = true
+
+ var glucoseHighAlarmEnabled: Boolean = true
+ var glucoseHighAlarmThreshold: Int = 250
+ var glucoseLowAlarmThreshold: Int = 60
+
+ var rateAlarmEnabled: Boolean = true
+ var rateFallingAlarmEnabled: Boolean = true
+ var rateFallingAlarmThreshold: Double = 1.5
+ var rateRisingAlarmEnabled: Boolean = true
+ var rateRisingAlarmThreshold: Double = 1.5
+
+ var predictiveAlarmEnabled: Boolean = true
+ var predictiveHighAlarmEnabled: Boolean = true
+ var predictiveHighAlarmThreshold: Int = 180
+ var predictiveHighAlarmMinutes: Int = 5
+ var predictiveLowAlarmEnabled: Boolean = true
+ var predictiveLowAlarmThreshold: Int = 70
+ var predictiveLowAlarmMinutes: Int = 5
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversenseBasePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversenseBasePacket.kt
new file mode 100644
index 00000000000..275db5260f5
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversenseBasePacket.kt
@@ -0,0 +1,56 @@
+package com.nightscout.eversense.packets
+
+import com.nightscout.eversense.EversenseLogger
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.e3.EversenseE3Packets
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+abstract class EversenseBasePacket : Object() {
+ abstract fun getRequestData(): ByteArray
+ abstract fun parseResponse(): Response?
+
+ protected var receivedData = UByteArray(0)
+
+ fun getAnnotation(): EversensePacket? {
+ return this.javaClass.annotations.find { it.annotationClass == EversensePacket::class } as? EversensePacket
+ }
+
+ protected fun getStartIndex(): Int {
+ val annotation = getAnnotation() ?:run {
+ EversenseLogger.error("EversenseBasePacket", this.javaClass.name + " does not have the EversensePacket annotation...")
+ return 0
+ }
+
+ return when(annotation.responseId) {
+ EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ EversenseE3Packets.ReadTwoByteSerialFlashRegisterResponseId,
+ EversenseE3Packets.ReadFourByteSerialFlashRegisterResponseId -> 4
+
+ else -> 1
+ }
+ }
+
+ fun appendData(data: UByteArray) {
+ receivedData += data
+ }
+
+ fun buildRequest(): ByteArray? {
+ val annotation = getAnnotation() ?:run {
+ EversenseLogger.error("EversenseBasePacket", this.javaClass.name + " does not have the EversensePacket annotation...")
+ return null
+ }
+
+ if (annotation.securityType == EversenseSecurityType.None) {
+ var requestData = byteArrayOf(annotation.requestId)
+ requestData += this.getRequestData()
+ requestData += EversenseE3Writer.generateChecksumCRC16(requestData)
+
+ return requestData
+ } else {
+ EversenseLogger.error("EversenseBasePacket", "TODO: Implement Eversense 365 request builder...")
+ return null
+ }
+ }
+
+ abstract class Response {}
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversenseE3Communicator.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversenseE3Communicator.kt
new file mode 100644
index 00000000000..d6e7bc766c8
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversenseE3Communicator.kt
@@ -0,0 +1,217 @@
+package com.nightscout.eversense.packets
+
+import android.content.SharedPreferences
+import androidx.core.content.edit
+import com.nightscout.eversense.EversenseGattCallback
+import com.nightscout.eversense.EversenseLogger
+import com.nightscout.eversense.StorageKeys
+import com.nightscout.eversense.callbacks.EversenseWatcher
+import com.nightscout.eversense.enums.EversenseType
+import com.nightscout.eversense.models.EversenseCGMResult
+import com.nightscout.eversense.models.EversenseState
+import com.nightscout.eversense.models.EversenseTransmitterSettings
+import com.nightscout.eversense.packets.e3.GetBatteryPercentagePacket
+import com.nightscout.eversense.packets.e3.GetCurrentDatetimePacket
+import com.nightscout.eversense.packets.e3.GetCurrentGlucosePacket
+import com.nightscout.eversense.packets.e3.GetInsertionDatePacket
+import com.nightscout.eversense.packets.e3.GetInsertionTimePacket
+import com.nightscout.eversense.packets.e3.GetSettingGlucoseHighEnabled
+import com.nightscout.eversense.packets.e3.GetSettingGlucoseHighThresholdPacket
+import com.nightscout.eversense.packets.e3.GetSettingGlucoseLowThresholdPacket
+import com.nightscout.eversense.packets.e3.GetSettingPredictiveAlarmEnabledPacket
+import com.nightscout.eversense.packets.e3.GetSettingPredictiveHighEnabledPacket
+import com.nightscout.eversense.packets.e3.GetSettingPredictiveHighThresholdPacket
+import com.nightscout.eversense.packets.e3.GetSettingPredictiveHighTimePacket
+import com.nightscout.eversense.packets.e3.GetSettingPredictiveLowEnabledPacket
+import com.nightscout.eversense.packets.e3.GetSettingPredictiveLowThresholdPacket
+import com.nightscout.eversense.packets.e3.GetSettingPredictiveLowTimePacket
+import com.nightscout.eversense.packets.e3.GetSettingRateEnabledPacket
+import com.nightscout.eversense.packets.e3.GetSettingRateFallingEnabledPacket
+import com.nightscout.eversense.packets.e3.GetSettingRateFallingThresholdPacket
+import com.nightscout.eversense.packets.e3.GetSettingRateRisingEnabledPacket
+import com.nightscout.eversense.packets.e3.GetSettingRateRisingThresholdPacket
+import com.nightscout.eversense.packets.e3.GetSettingVibratePacket
+import com.nightscout.eversense.packets.e3.SetCurrentDatetimePacket
+import com.nightscout.eversense.packets.e3.SetSettingGlucoseHighEnablePacket
+import com.nightscout.eversense.packets.e3.SetSettingGlucoseHighThresholdPacket
+import com.nightscout.eversense.packets.e3.SetSettingGlucoseLowThresholdPacket
+import com.nightscout.eversense.packets.e3.SetSettingPredictiveAlarmEnabledPacket
+import com.nightscout.eversense.packets.e3.SetSettingPredictiveHighAlarmEnabledPacket
+import com.nightscout.eversense.packets.e3.SetSettingPredictiveHighThresholdPacket
+import com.nightscout.eversense.packets.e3.SetSettingPredictiveHighTimePacket
+import com.nightscout.eversense.packets.e3.SetSettingPredictiveLowAlarmEnabledPacket
+import com.nightscout.eversense.packets.e3.SetSettingPredictiveLowThresholdPacket
+import com.nightscout.eversense.packets.e3.SetSettingPredictiveLowTimePacket
+import com.nightscout.eversense.packets.e3.SetSettingRateEnabledPacket
+import com.nightscout.eversense.packets.e3.SetSettingRateFallingEnabledPacket
+import com.nightscout.eversense.packets.e3.SetSettingRateFallingThresholdPacket
+import com.nightscout.eversense.packets.e3.SetSettingRateRisingEnabledPacket
+import com.nightscout.eversense.packets.e3.SetSettingRateRisingThresholdPacket
+import com.nightscout.eversense.packets.e3.SetSettingVibratePacket
+import kotlinx.serialization.json.Json
+import java.util.concurrent.TimeUnit
+
+class EversenseE3Communicator {
+ companion object {
+ private const val TAG = "EversenseE3Communicator"
+ private val JSON = Json { ignoreUnknownKeys = true }
+
+ fun readGlucose(gatt: EversenseGattCallback, preferences: SharedPreferences, watchers: List) {
+ val stateJson = preferences.getString(StorageKeys.STATE, null) ?: "{}"
+ val state = JSON.decodeFromString(stateJson)
+ val fourHalfMinAgo = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(270)
+
+ if (fourHalfMinAgo < state.recentGlucoseDatetime) {
+ EversenseLogger.warning(TAG, "Glucose data is still recent - lastReading: ${state.recentGlucoseDatetime}")
+ return
+ }
+
+ try {
+ EversenseLogger.debug(TAG, "Reading current glucose...")
+ val currentGlucose = gatt.writePacket(GetCurrentGlucosePacket())
+ if (currentGlucose.datetime <= state.recentGlucoseDatetime) {
+ EversenseLogger.warning(TAG, "Glucose data is still recent after reading - currentReading: ${currentGlucose.datetime}, lastReading: ${state.recentGlucoseDatetime}")
+ return
+ }
+
+ if (currentGlucose.glucoseInMgDl > 1000) {
+ EversenseLogger.error(TAG, "recentGlucose exceeds range - received: ${currentGlucose.glucoseInMgDl}")
+ return
+ }
+
+ val result = mutableListOf()
+ state.recentGlucoseDatetime = currentGlucose.datetime
+ state.recentGlucoseValue = currentGlucose.glucoseInMgDl
+ result += EversenseCGMResult(
+ glucoseInMgDl = currentGlucose.glucoseInMgDl,
+ datetime = currentGlucose.datetime,
+ trend = currentGlucose.trend
+ )
+
+ // TODO: read history for backfill
+
+ preferences.edit(commit = true) {
+ putString(StorageKeys.STATE, JSON.encodeToString(state))
+ }
+
+ watchers.forEach {
+ it.onCGMRead(EversenseType.EVERSENSE_E3, result)
+ }
+ } catch (exception: Exception) {
+ EversenseLogger.error(TAG, "Got exception during readGlucose - exception $exception")
+ }
+ }
+
+ fun fullSync(gatt: EversenseGattCallback, preferences: SharedPreferences, watchers: List) {
+ try {
+ val stateJson = preferences.getString(StorageKeys.STATE, null) ?: "{}"
+ val state = JSON.decodeFromString(stateJson)
+ val fourHalfMinAgo = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(270)
+
+ if (fourHalfMinAgo < state.lastSync) {
+ EversenseLogger.warning(TAG, "State is still fresh - lastSync: ${state.lastSync}")
+ return
+ }
+
+ EversenseLogger.debug(TAG, "Reading current datetime...")
+ val currentDatetime = gatt.writePacket(GetCurrentDatetimePacket())
+ if (currentDatetime.needsTimeSync) {
+ EversenseLogger.debug(TAG, "Send SetCurrentDatetimePacket...")
+ gatt.writePacket(SetCurrentDatetimePacket())
+ }
+
+ EversenseLogger.debug(TAG, "Reading battery percentage...")
+ val batteryPercentage = gatt.writePacket(GetBatteryPercentagePacket())
+ state.batteryPercentage = batteryPercentage.percentage
+
+ EversenseLogger.debug(TAG, "Reading insertion datetime...")
+ val insertionDate = gatt.writePacket(GetInsertionDatePacket())
+ val insertionTime = gatt.writePacket(GetInsertionTimePacket())
+ state.insertionDate = insertionDate.date + insertionTime.time
+
+ // Transmitter settings
+ EversenseLogger.debug(TAG, "Reading transmitter settings...")
+ val vibrateEnabled = gatt.writePacket(GetSettingVibratePacket())
+ val glucoseHighEnabled = gatt.writePacket(GetSettingGlucoseHighEnabled())
+ val glucoseHighThreshold = gatt.writePacket(GetSettingGlucoseHighThresholdPacket())
+ val glucoseLowThreshold = gatt.writePacket(GetSettingGlucoseLowThresholdPacket())
+ val rateEnabled = gatt.writePacket(GetSettingRateEnabledPacket())
+ val rateFallingEnabled = gatt.writePacket(GetSettingRateFallingEnabledPacket())
+ val rateFallingThreshold = gatt.writePacket(GetSettingRateFallingThresholdPacket())
+ val rateRisingEnabled = gatt.writePacket(GetSettingRateRisingEnabledPacket())
+ val rateRisingThreshold = gatt.writePacket(GetSettingRateRisingThresholdPacket())
+ val predictiveEnabled = gatt.writePacket(GetSettingPredictiveAlarmEnabledPacket())
+ val predictiveHighEnabled = gatt.writePacket(GetSettingPredictiveHighEnabledPacket())
+ val predictiveHighTime = gatt.writePacket(GetSettingPredictiveHighTimePacket())
+ val predictiveHighThreshold = gatt.writePacket(GetSettingPredictiveHighThresholdPacket())
+ val predictiveLowEnabled = gatt.writePacket(GetSettingPredictiveLowEnabledPacket())
+ val predictiveLowTime = gatt.writePacket(GetSettingPredictiveLowTimePacket())
+ val predictiveLowThreshold = gatt.writePacket(GetSettingPredictiveLowThresholdPacket())
+
+ state.settings.vibrateEnabled = vibrateEnabled.enabled
+ state.settings.glucoseHighAlarmEnabled = glucoseHighEnabled.enabled
+ state.settings.glucoseHighAlarmThreshold = glucoseHighThreshold.threshold
+ state.settings.glucoseLowAlarmThreshold = glucoseLowThreshold.threshold
+ state.settings.rateAlarmEnabled = rateEnabled.enabled
+ state.settings.rateFallingAlarmEnabled = rateFallingEnabled.enabled
+ state.settings.rateFallingAlarmThreshold = rateFallingThreshold.threshold
+ state.settings.rateRisingAlarmEnabled = rateRisingEnabled.enabled
+ state.settings.rateRisingAlarmThreshold = rateRisingThreshold.threshold
+ state.settings.predictiveAlarmEnabled = predictiveEnabled.enabled
+ state.settings.predictiveHighAlarmEnabled = predictiveHighEnabled.enabled
+ state.settings.predictiveHighAlarmMinutes = predictiveHighTime.minutes
+ state.settings.predictiveHighAlarmThreshold = predictiveHighThreshold.threshold
+ state.settings.predictiveLowAlarmEnabled = predictiveLowEnabled.enabled
+ state.settings.predictiveLowAlarmMinutes = predictiveLowTime.minutes
+ state.settings.predictiveLowAlarmThreshold = predictiveLowThreshold.threshold
+
+ state.lastSync = System.currentTimeMillis()
+ EversenseLogger.info(TAG, "Completed full sync - datetime: ${state.lastSync}")
+ preferences.edit(commit = true) {
+ putString(StorageKeys.STATE, JSON.encodeToString(state))
+ }
+ watchers.forEach {
+ it.onStateChanged(state)
+ }
+ } catch (exception: Exception) {
+ EversenseLogger.error(TAG, "Failed to do full sync: $exception")
+ }
+ }
+
+ fun writeSettings(gatt: EversenseGattCallback, preferences: SharedPreferences, settings: EversenseTransmitterSettings): Boolean {
+ try {
+ gatt.writePacket(SetSettingVibratePacket(settings.vibrateEnabled))
+
+ gatt.writePacket(SetSettingGlucoseHighEnablePacket(settings.glucoseHighAlarmEnabled))
+ gatt.writePacket(SetSettingGlucoseHighThresholdPacket(settings.glucoseHighAlarmThreshold))
+ gatt.writePacket(SetSettingGlucoseLowThresholdPacket(settings.glucoseLowAlarmThreshold))
+
+ gatt.writePacket(SetSettingRateEnabledPacket(settings.rateAlarmEnabled))
+ gatt.writePacket(SetSettingRateFallingEnabledPacket(settings.rateFallingAlarmEnabled))
+ gatt.writePacket(SetSettingRateFallingThresholdPacket(settings.rateFallingAlarmThreshold))
+ gatt.writePacket(SetSettingRateRisingEnabledPacket(settings.rateRisingAlarmEnabled))
+ gatt.writePacket(SetSettingRateRisingThresholdPacket(settings.rateRisingAlarmThreshold))
+
+ gatt.writePacket(SetSettingPredictiveAlarmEnabledPacket(settings.predictiveAlarmEnabled))
+ gatt.writePacket(SetSettingPredictiveHighAlarmEnabledPacket(settings.predictiveHighAlarmEnabled))
+ gatt.writePacket(SetSettingPredictiveHighTimePacket(settings.predictiveHighAlarmMinutes))
+ gatt.writePacket(SetSettingPredictiveHighThresholdPacket(settings.predictiveHighAlarmThreshold))
+ gatt.writePacket(SetSettingPredictiveLowAlarmEnabledPacket(settings.predictiveLowAlarmEnabled))
+ gatt.writePacket(SetSettingPredictiveLowTimePacket(settings.predictiveLowAlarmMinutes))
+ gatt.writePacket(SetSettingPredictiveLowThresholdPacket(settings.predictiveLowAlarmThreshold))
+
+ val stateJson = preferences.getString(StorageKeys.STATE, null) ?: "{}"
+ val state = JSON.decodeFromString(stateJson)
+ state.settings = settings
+ preferences.edit(commit = true) {
+ putString(StorageKeys.STATE, JSON.encodeToString(state))
+ }
+
+ return true
+ } catch (exception: Exception) {
+ EversenseLogger.error(TAG, "Failed to do full sync: $exception")
+ return false
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversensePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversensePacket.kt
new file mode 100644
index 00000000000..dc04fe3d96a
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/EversensePacket.kt
@@ -0,0 +1,17 @@
+package com.nightscout.eversense.packets
+
+import com.nightscout.eversense.enums.EversenseSecurityType
+
+annotation class EversensePacket(
+ /** The request id for the packet */
+ val requestId: Byte,
+
+ /** The expected response id for this packet */
+ val responseId: Byte,
+
+ /** The expected response id for this packet. Only relevant for 365 packets */
+ val responseType: Byte,
+
+ /** The required security protocol */
+ val securityType: EversenseSecurityType
+)
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/EversenseE3Packets.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/EversenseE3Packets.kt
new file mode 100644
index 00000000000..0d9d1cb75c1
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/EversenseE3Packets.kt
@@ -0,0 +1,117 @@
+package com.nightscout.eversense.packets.e3
+
+class EversenseE3Packets {
+ companion object {
+ const val AssertSnoozeAgainsAlarmCommandId = 20.toByte()
+ const val AssertSnoozeAgainsAlarmResponseId = 148.toByte()
+ const val CalibrationAlertPush = 77.toByte()
+ const val CalibrationPush = 67.toByte()
+ const val CalibrationSwitchPush = 76.toByte()
+ const val ChangeTimingParametersCommandId = 117.toByte()
+ const val ChangeTimingParametersResponseId = 245.toByte()
+ const val ClearErrorFlagsCommandId = 4.toByte()
+ const val ClearErrorFlagsResponseId = 132.toByte()
+ const val DisconnectBLESavingBondingInformationCommandId = 116.toByte()
+ const val DisconnectBLESavingBondingInformationResponseId = 244.toByte()
+ const val EnterDiagnosticModeCommandId = 118.toByte()
+ const val EnterDiagnosticModeResponseId = 246.toByte()
+ const val ErrorResponseId = 128.toByte()
+ const val ExerciseVibrationCommandId = 106.toByte()
+ const val ExerciseVibrationResponseId = 234.toByte()
+ const val ExitDiagnosticModeCommandId = 119.toByte()
+ const val ExitDiagnosticModeResponseId = 247.toByte()
+ const val GlucoseLevelAlarmPush = 64.toByte()
+ const val GlucoseLevelAlertPush = 65.toByte()
+ const val HardwareStatusPush = 69.toByte()
+ const val KeepAlivePush = 80.toByte()
+ const val LinkTransmitterWithSensorCommandId = 2.toByte()
+ const val LinkTransmitterWithSensorResponseId = 130.toByte()
+ const val MarkPatientEventRecordAsDeletedCommandId = 29.toByte()
+ const val MarkPatientEventRecordAsDeletedResponseId = 157.toByte()
+ const val PingCommandId = 1.toByte()
+ const val PingResponseId = 129.toByte()
+ const val RateAndPredictiveAlertPush = 66.toByte()
+ const val ReadAllAvailableSensorsResponseId = 134.toByte()
+ const val ReadAllSensorGlucoseAlertsInSpecifiedRangeCommandId = 113.toByte()
+ const val ReadAllSensorGlucoseAlertsInSpecifiedRangeResponseId = 241.toByte()
+ const val ReadAllSensorGlucoseDataInSpecifiedRangeCommandId = 112.toByte()
+ const val ReadAllSensorGlucoseDataInSpecifiedRangeResponseId = 240.toByte()
+ const val ReadCurrentTransmitterDateAndTimeCommandId = 25.toByte()
+ const val ReadCurrentTransmitterDateAndTimeResponseId = 153.toByte()
+ const val ReadFirstAndLastBloodGlucoseDataRecordNumbersCommandId = 23.toByte()
+ const val ReadFirstAndLastBloodGlucoseDataRecordNumbersResponseId = 151.toByte()
+ const val ReadFirstAndLastErrorLogRecordNumbersCommandId = 39.toByte()
+ const val ReadFirstAndLastErrorLogRecordNumbersResponseId = 167.toByte()
+ const val ReadFirstAndLastMiscEventLogRecordNumbersCommandId = 35.toByte()
+ const val ReadFirstAndLastMiscEventLogRecordNumbersResponseId = 163.toByte()
+ const val ReadFirstAndLastPatientEventRecordNumbersCommandId = 28.toByte()
+ const val ReadFirstAndLastPatientEventRecordNumbersResponseId = 156.toByte()
+ const val ReadFirstAndLastSensorGlucoseAlertRecordNumbersCommandId = 18.toByte()
+ const val ReadFirstAndLastSensorGlucoseAlertRecordNumbersResponseId = 146.toByte()
+ const val ReadFirstAndLastSensorGlucoseRecordNumbersCommandId = 14.toByte()
+ const val ReadFirstAndLastSensorGlucoseRecordNumbersResponseId = 142.toByte()
+ const val ReadFourByteSerialFlashRegisterCommandId = 46.toByte()
+ const val ReadFourByteSerialFlashRegisterResponseId = 174.toByte()
+ const val ReadLogOfBloodGlucoseDataInSpecifiedRangeCommandId = 114.toByte()
+ const val ReadLogOfBloodGlucoseDataInSpecifiedRangeResponseId = 242.toByte()
+ const val ReadLogOfPatientEventsInSpecifiedRangeCommandId = 115.toByte()
+ const val ReadLogOfPatientEventsInSpecifiedRangeResponseId = 243.toByte()
+ const val ReadNByteSerialFlashRegisterCommandId = 48.toByte()
+ const val ReadNByteSerialFlashRegisterResponseId = 176.toByte()
+ const val ReadSensorGlucoseAlertsAndStatusCommandId = 16.toByte()
+ const val ReadSensorGlucoseAlertsAndStatusResponseId = 144.toByte()
+ const val ReadSensorGlucoseCommandId = 8.toByte()
+ const val ReadSensorGlucoseResponseId = 136.toByte()
+ const val ReadSingleBloodGlucoseDataRecordCommandId = 22.toByte()
+ const val ReadSingleBloodGlucoseDataRecordResponseId = 150.toByte()
+ const val ReadSingleByteSerialFlashRegisterCommandId = 42.toByte()
+ const val ReadSingleByteSerialFlashRegisterResponseId = 170.toByte()
+ const val ReadSingleMiscEventLogCommandId = 34.toByte()
+ const val ReadSingleMiscEventLogResponseId = 162.toByte()
+ const val ReadSinglePatientEventCommandId = 27.toByte()
+ const val ReadSinglePatientEventResponseId = 155.toByte()
+ const val ReadSingleSensorGlucoseAlertRecordCommandId = 17.toByte()
+ const val ReadSingleSensorGlucoseAlertRecordResponseId = 145.toByte()
+ const val ReadSingleSensorGlucoseDataRecordResponseId = 137.toByte()
+ const val ReadTwoByteSerialFlashRegisterCommandId = 44.toByte()
+ const val ReadTwoByteSerialFlashRegisterResponseId = 172.toByte()
+ const val ResetTransmitterCommandId = 3.toByte()
+ const val ResetTransmitterResponseId = 131.toByte()
+ const val SaveBLEBondingInformationCommandId = 105.toByte()
+ const val SaveBLEBondingInformationResponseId = 233.toByte()
+ const val SendBloodGlucoseDataCommandId = 21.toByte()
+ const val SendBloodGlucoseDataResponseId = 149.toByte()
+ const val SendBloodGlucoseDataWithTwoTimestampsCommandId = 60.toByte()
+ const val SendBloodGlucoseDataWithTwoTimestampsResponseId = 188.toByte()
+ const val SensorReadAlertPush = 73.toByte()
+ const val SensorReplacement2Push = 75.toByte()
+ const val SensorReplacementPush = 68.toByte()
+ const val SetCurrentTransmitterDateAndTimeCommandId = 7.toByte()
+ const val SetCurrentTransmitterDateAndTimeResponseId = 135.toByte()
+ const val StartSelfTestSequenceCommandId = 5.toByte()
+ const val StartSelfTestSequenceResponseId = 133.toByte()
+ const val TestResponseId = 224.toByte()
+ const val TransmitterBatteryPush = 71.toByte()
+ const val TransmitterEOLPush = 74.toByte()
+ const val WriteFourByteSerialFlashRegisterCommandId = 47.toByte()
+ const val WriteFourByteSerialFlashRegisterResponseId = 175.toByte()
+ const val WriteNByteSerialFlashRegisterCommandId = 49.toByte()
+ const val WriteNByteSerialFlashRegisterResponseId = 177.toByte()
+ const val WritePatientEventCommandId = 26.toByte()
+ const val WritePatientEventResponseId = 154.toByte()
+ const val WriteSingleByteSerialFlashRegisterCommandId = 43.toByte()
+ const val WriteSingleByteSerialFlashRegisterResponseId = 171.toByte()
+ const val WriteSingleMiscEventLogRecordCommandId = 36.toByte()
+ const val WriteSingleMiscEventLogRecordResponseId = 164.toByte()
+ const val WriteTwoByteSerialFlashRegisterCommandId = 45.toByte()
+ const val WriteTwoByteSerialFlashRegisterResponseId = 173.toByte()
+
+ fun isPushPacket(data: Byte): Boolean {
+ return data == KeepAlivePush
+ }
+
+ fun isErrorPacket(data: Byte): Boolean {
+ return data == ErrorResponseId
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetBatteryPercentagePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetBatteryPercentagePacket.kt
new file mode 100644
index 00000000000..aa5743a8fdf
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetBatteryPercentagePacket.kt
@@ -0,0 +1,47 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetBatteryPercentagePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.BatteryPercentage.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(percentage = parseEnum(receivedData[getStartIndex()].toInt()))
+ }
+
+ private fun parseEnum(value: Int): Int {
+ return when(value) {
+ 0 -> 0 //%
+ 1 -> 5 //%
+ 2 -> 10 //%
+ 3 -> 25 //%
+ 4 -> 35 //%
+ 5 -> 45 //%
+ 6 -> 55 //%
+ 7 -> 65 //%
+ 8 -> 75 //%
+ 9 -> 85 //%
+ 10 -> 95 //%
+ 11 -> 100 //%
+ else -> -1
+ }
+ }
+
+ data class Response(val percentage: Int) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetCurrentDatetimePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetCurrentDatetimePacket.kt
new file mode 100644
index 00000000000..46b77105bd3
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetCurrentDatetimePacket.kt
@@ -0,0 +1,49 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.EversenseLogger
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+import java.util.TimeZone
+import kotlin.math.abs
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadCurrentTransmitterDateAndTimeCommandId,
+ responseId = EversenseE3Packets.ReadCurrentTransmitterDateAndTimeResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetCurrentDatetimePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return ByteArray(0)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ val start = getStartIndex()
+ val date = EversenseE3Parser.readDate(receivedData, start)
+ val time = EversenseE3Parser.readTime(receivedData, start + 2)
+ val timeZoneOffset = EversenseE3Parser.readTimezone(receivedData, start + 4)
+
+ var needsTimeSync = false
+ val actualTimeZoneOffset = TimeZone.getDefault().getOffset(System.currentTimeMillis()).toLong()
+
+ // Allow time drift <10s
+ if (abs(System.currentTimeMillis() - (date + time)) > 10_000) {
+ EversenseLogger.warning("GetCurrentDatetimePacket", "time drift detected... drift: ${abs(System.currentTimeMillis() - (date + time))} ms")
+ needsTimeSync = true
+ } else if (actualTimeZoneOffset != timeZoneOffset) {
+ EversenseLogger.warning("GetCurrentDatetimePacket", "timezone mismatch - received: $timeZoneOffset, actual: $actualTimeZoneOffset")
+ needsTimeSync = true
+ }
+
+ return Response(date + time, timeZoneOffset, needsTimeSync)
+ }
+
+ data class Response(val datetime: Long, val timezoneOffset: Long, val needsTimeSync: Boolean): EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetCurrentGlucosePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetCurrentGlucosePacket.kt
new file mode 100644
index 00000000000..c3f63b2af77
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetCurrentGlucosePacket.kt
@@ -0,0 +1,45 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.enums.EversenseTrendArrow
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSensorGlucoseCommandId,
+ responseId = EversenseE3Packets.ReadSensorGlucoseResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetCurrentGlucosePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return ByteArray(0)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(
+ datetime = EversenseE3Parser.readDate(receivedData, 4) + EversenseE3Parser.readTime(receivedData, 6),
+ glucoseInMgDl = EversenseE3Parser.readGlucose(receivedData, 9),
+ trend = parseTrend(receivedData[13].toInt()),
+ )
+ }
+
+ private fun parseTrend(value: Int): EversenseTrendArrow {
+ return when(value) {
+ 1 -> EversenseTrendArrow.SINGLE_DOWN
+ 2 -> EversenseTrendArrow.FORTY_FIVE_DOWN
+ 4 -> EversenseTrendArrow.FLAT
+ 8 -> EversenseTrendArrow.FORTY_FIVE_UP
+ 16 -> EversenseTrendArrow.SINGLE_UP
+ else -> EversenseTrendArrow.NONE
+ }
+ }
+
+ data class Response(val datetime: Long, val glucoseInMgDl: Int, val trend: EversenseTrendArrow) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetInsertionDatePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetInsertionDatePacket.kt
new file mode 100644
index 00000000000..4ab0e89d9e1
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetInsertionDatePacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetInsertionDatePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.SensorInsertionDate.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(date = EversenseE3Parser.readDate(receivedData, getStartIndex()))
+ }
+
+ data class Response(val date: Long) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetInsertionTimePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetInsertionTimePacket.kt
new file mode 100644
index 00000000000..6bac00734e4
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetInsertionTimePacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetInsertionTimePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.SensorInsertionTime.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(time = EversenseE3Parser.readTime(receivedData, getStartIndex()))
+ }
+
+ data class Response(val time: Long) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseHighEnabled.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseHighEnabled.kt
new file mode 100644
index 00000000000..0c05e0088a4
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseHighEnabled.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingGlucoseHighEnabled : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.HighGlucoseAlarmEnabled.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(enabled = receivedData[getStartIndex()].toInt() == 0x55)
+ }
+
+ data class Response(val enabled: Boolean) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseHighThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseHighThresholdPacket.kt
new file mode 100644
index 00000000000..30e4fe49bf3
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseHighThresholdPacket.kt
@@ -0,0 +1,32 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingGlucoseHighThresholdPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.HighGlucoseAlarmThreshold.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(
+ threshold = EversenseE3Parser.readGlucose(receivedData, getStartIndex())
+ )
+ }
+
+ data class Response(val threshold: Int) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseLowThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseLowThresholdPacket.kt
new file mode 100644
index 00000000000..59b2820d1fd
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingGlucoseLowThresholdPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingGlucoseLowThresholdPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.LowGlucoseAlarmThreshold.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(threshold = EversenseE3Parser.readGlucose(receivedData, getStartIndex()))
+ }
+
+ data class Response(val threshold: Int): EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveAlarmEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveAlarmEnabledPacket.kt
new file mode 100644
index 00000000000..aa0d93bb93b
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveAlarmEnabledPacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingPredictiveAlarmEnabledPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveAlert.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(enabled = receivedData[getStartIndex()].toInt() == 0x55)
+ }
+
+ data class Response(val enabled: Boolean) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighEnabledPacket.kt
new file mode 100644
index 00000000000..34d8a0f43de
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighEnabledPacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingPredictiveHighEnabledPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveHighAlert.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(enabled = receivedData[getStartIndex()].toInt() == 0x55)
+ }
+
+ data class Response(val enabled: Boolean) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighThresholdPacket.kt
new file mode 100644
index 00000000000..f84fef95d79
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighThresholdPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingPredictiveHighThresholdPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveHighTarget.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(threshold = EversenseE3Parser.readGlucose(receivedData, getStartIndex()))
+ }
+
+ data class Response(val threshold: Int) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighTimePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighTimePacket.kt
new file mode 100644
index 00000000000..449e34de813
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveHighTimePacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingPredictiveHighTimePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveHighTime.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(minutes = receivedData[getStartIndex()].toInt())
+ }
+
+ data class Response(val minutes: Int) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowEnabledPacket.kt
new file mode 100644
index 00000000000..4da3220c867
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowEnabledPacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingPredictiveLowEnabledPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveLowAlert.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(enabled = receivedData[getStartIndex()].toInt() == 0x55)
+ }
+
+ data class Response(val enabled: Boolean) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowThresholdPacket.kt
new file mode 100644
index 00000000000..65a10358adc
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowThresholdPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingPredictiveLowThresholdPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveLowTarget.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(threshold = EversenseE3Parser.readGlucose(receivedData, getStartIndex()))
+ }
+
+ data class Response(val threshold: Int) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowTimePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowTimePacket.kt
new file mode 100644
index 00000000000..92118b40063
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingPredictiveLowTimePacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingPredictiveLowTimePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveLowTime.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(minutes = receivedData[getStartIndex()].toInt())
+ }
+
+ data class Response(val minutes: Int) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateEnabledPacket.kt
new file mode 100644
index 00000000000..7830f3f05ec
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateEnabledPacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingRateEnabledPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateAlert.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(enabled = receivedData[getStartIndex()].toInt() == 0x55)
+ }
+
+ data class Response(val enabled: Boolean) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateFallingEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateFallingEnabledPacket.kt
new file mode 100644
index 00000000000..abe2049069a
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateFallingEnabledPacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingRateFallingEnabledPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateFallingAlert.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(enabled = receivedData[getStartIndex()].toInt() == 0x55)
+ }
+
+ data class Response(val enabled: Boolean) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateFallingThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateFallingThresholdPacket.kt
new file mode 100644
index 00000000000..367970389c2
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateFallingThresholdPacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingRateFallingThresholdPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateFallingThreshold.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(threshold = receivedData[getStartIndex()].toDouble() / 10)
+ }
+
+ data class Response(val threshold: Double): EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateRisingEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateRisingEnabledPacket.kt
new file mode 100644
index 00000000000..68d1f6df7a4
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateRisingEnabledPacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingRateRisingEnabledPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateRisingAlert.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(enabled = receivedData[getStartIndex()].toInt() == 0x55)
+ }
+
+ data class Response(val enabled: Boolean) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateRisingThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateRisingThresholdPacket.kt
new file mode 100644
index 00000000000..01e110c392e
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingRateRisingThresholdPacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingRateRisingThresholdPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateRisingThreshold.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(threshold = receivedData[getStartIndex()].toDouble() / 10)
+ }
+
+ data class Response(val threshold: Double) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingVibratePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingVibratePacket.kt
new file mode 100644
index 00000000000..babdee49d2c
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/GetSettingVibratePacket.kt
@@ -0,0 +1,31 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.ReadSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class GetSettingVibratePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.VibrateMode.getRequestData()
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response(
+ enabled = receivedData[getStartIndex()].toInt() == 0x55
+ )
+ }
+
+ data class Response(val enabled: Boolean) : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SaveBondingInformationPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SaveBondingInformationPacket.kt
new file mode 100644
index 00000000000..588a359d7e9
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SaveBondingInformationPacket.kt
@@ -0,0 +1,28 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.SaveBLEBondingInformationCommandId,
+ responseId = EversenseE3Packets.SaveBLEBondingInformationResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SaveBondingInformationPacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return ByteArray(0)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return SaveBondingInformationResponse()
+ }
+
+ class SaveBondingInformationResponse() : Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetCurrentDatetimePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetCurrentDatetimePacket.kt
new file mode 100644
index 00000000000..e1c45c77a00
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetCurrentDatetimePacket.kt
@@ -0,0 +1,32 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.SetCurrentTransmitterDateAndTimeCommandId,
+ responseId = EversenseE3Packets.SetCurrentTransmitterDateAndTimeResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetCurrentDatetimePacket : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ val now = System.currentTimeMillis()
+ return EversenseE3Writer.writeDate(now) +
+ EversenseE3Writer.writeTime(now) +
+ EversenseE3Writer.writeTimezone(now)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response() {}
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseHighEnablePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseHighEnablePacket.kt
new file mode 100644
index 00000000000..91ffa1f2b93
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseHighEnablePacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingGlucoseHighEnablePacket(private val enabled: Boolean) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.HighGlucoseAlarmEnabled.getRequestData() + EversenseE3Writer.writeBoolean(enabled)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response() : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseHighThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseHighThresholdPacket.kt
new file mode 100644
index 00000000000..e4620c5ea40
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseHighThresholdPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingGlucoseHighThresholdPacket(private val threshold: Int) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.HighGlucoseAlarmThreshold.getRequestData() + EversenseE3Writer.writeInt16(threshold)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response() : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseLowThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseLowThresholdPacket.kt
new file mode 100644
index 00000000000..02f64c23242
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingGlucoseLowThresholdPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingGlucoseLowThresholdPacket(private val threshold: Int) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.LowGlucoseAlarmThreshold.getRequestData() + EversenseE3Writer.writeInt16(threshold)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response() : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveAlarmEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveAlarmEnabledPacket.kt
new file mode 100644
index 00000000000..a92bb37fe6d
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveAlarmEnabledPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingPredictiveAlarmEnabledPacket(private val enabled: Boolean) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveAlert.getRequestData() + EversenseE3Writer.writeBoolean(enabled)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighAlarmEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighAlarmEnabledPacket.kt
new file mode 100644
index 00000000000..bbe4d10e355
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighAlarmEnabledPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingPredictiveHighAlarmEnabledPacket(private val enabled: Boolean) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveHighAlert.getRequestData() + EversenseE3Writer.writeBoolean(enabled)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighThresholdPacket.kt
new file mode 100644
index 00000000000..d3d91f127ce
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighThresholdPacket.kt
@@ -0,0 +1,31 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingPredictiveHighThresholdPacket(private val threshold: Int) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveHighTarget.getRequestData() + EversenseE3Writer.writeInt16(threshold)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighTimePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighTimePacket.kt
new file mode 100644
index 00000000000..61f0e39b535
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveHighTimePacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingPredictiveHighTimePacket(private val minutes: Int) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveHighTime.getRequestData() + byteArrayOf(minutes.toByte())
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowAlarmEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowAlarmEnabledPacket.kt
new file mode 100644
index 00000000000..b2464e355f4
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowAlarmEnabledPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingPredictiveLowAlarmEnabledPacket(private val enabled: Boolean) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveLowAlert.getRequestData() + EversenseE3Writer.writeBoolean(enabled)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowThresholdPacket.kt
new file mode 100644
index 00000000000..c4012e2eded
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowThresholdPacket.kt
@@ -0,0 +1,31 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Parser
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteTwoByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteTwoByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingPredictiveLowThresholdPacket(private val threshold: Int) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveLowTarget.getRequestData() + EversenseE3Writer.writeInt16(threshold)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowTimePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowTimePacket.kt
new file mode 100644
index 00000000000..11e45db2503
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingPredictiveLowTimePacket.kt
@@ -0,0 +1,29 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingPredictiveLowTimePacket(private val minutes: Int) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.PredictiveLowTime.getRequestData() + byteArrayOf(minutes.toByte())
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateEnabledPacket.kt
new file mode 100644
index 00000000000..c00810a5342
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateEnabledPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingRateEnabledPacket(private val enabled: Boolean) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateAlert.getRequestData() + EversenseE3Writer.writeBoolean(enabled)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateFallingEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateFallingEnabledPacket.kt
new file mode 100644
index 00000000000..ee2b0bb1418
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateFallingEnabledPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingRateFallingEnabledPacket(private val enabled: Boolean) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateFallingAlert.getRequestData() + EversenseE3Writer.writeBoolean(enabled)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateFallingThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateFallingThresholdPacket.kt
new file mode 100644
index 00000000000..fee749d1cac
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateFallingThresholdPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingRateFallingThresholdPacket(private val threshold: Double) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateFallingThreshold.getRequestData() + EversenseE3Writer.writeDouble(threshold)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateRisingEnabledPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateRisingEnabledPacket.kt
new file mode 100644
index 00000000000..b0aa477f8b3
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateRisingEnabledPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingRateRisingEnabledPacket(private val enabled: Boolean) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateRisingAlert.getRequestData() + EversenseE3Writer.writeBoolean(enabled)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateRisingThresholdPacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateRisingThresholdPacket.kt
new file mode 100644
index 00000000000..2d8d1da2200
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingRateRisingThresholdPacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingRateRisingThresholdPacket(private val threshold: Double) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.RateRisingThreshold.getRequestData() + EversenseE3Writer.writeDouble(threshold)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingVibratePacket.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingVibratePacket.kt
new file mode 100644
index 00000000000..9e9361b9438
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/SetSettingVibratePacket.kt
@@ -0,0 +1,30 @@
+package com.nightscout.eversense.packets.e3
+
+import com.nightscout.eversense.enums.EversenseE3Memory
+import com.nightscout.eversense.enums.EversenseSecurityType
+import com.nightscout.eversense.packets.EversenseBasePacket
+import com.nightscout.eversense.packets.EversensePacket
+import com.nightscout.eversense.packets.e3.util.EversenseE3Writer
+
+@EversensePacket(
+ requestId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterCommandId,
+ responseId = EversenseE3Packets.WriteSingleByteSerialFlashRegisterResponseId,
+ responseType = 0,
+ securityType = EversenseSecurityType.None
+)
+class SetSettingVibratePacket(private val enabled: Boolean) : EversenseBasePacket() {
+
+ override fun getRequestData(): ByteArray {
+ return EversenseE3Memory.VibrateMode.getRequestData() + EversenseE3Writer.writeBoolean(enabled)
+ }
+
+ override fun parseResponse(): Response? {
+ if (receivedData.isEmpty()) {
+ return null
+ }
+
+ return Response()
+ }
+
+ class Response : EversenseBasePacket.Response()
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/util/EversenseE3Parser.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/util/EversenseE3Parser.kt
new file mode 100644
index 00000000000..bfc6fd339d5
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/util/EversenseE3Parser.kt
@@ -0,0 +1,69 @@
+package com.nightscout.eversense.packets.e3.util
+
+import android.util.Log
+import java.util.Calendar
+import java.util.TimeZone
+
+class EversenseE3Parser {
+ companion object {
+ fun readDate(data: UByteArray, start: Int): Long {
+ val lowBit = data[start].toInt()
+ val highBit = data[start+1].toInt()
+
+ val day = lowBit and 31
+ var month = lowBit shr 5
+ val year = (highBit shr 1) + 2000
+
+ if (highBit and 1 == 1) {
+ month += 8
+ }
+
+ val calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
+ calendar.set(Calendar.YEAR, year)
+ calendar.set(Calendar.MONTH, month - 1)
+ calendar.set(Calendar.DAY_OF_MONTH, day)
+ calendar.set(Calendar.HOUR_OF_DAY, 0)
+ calendar.set(Calendar.MINUTE, 0)
+ calendar.set(Calendar.SECOND, 0)
+ calendar.set(Calendar.MILLISECOND, 0)
+
+ return calendar.timeInMillis
+ }
+
+ fun readTime(data: UByteArray, start: Int): Long {
+ val lowBit = data[start].toInt()
+ val highBit = data[start+1].toInt()
+
+ val hour = highBit shr 3
+ val minute = ((highBit and 7) shl 3) or (lowBit shr 5)
+ val second = (lowBit and 31) * 2
+
+
+ val calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
+ calendar.set(Calendar.YEAR, 1970)
+ calendar.set(Calendar.MONTH, 0)
+ calendar.set(Calendar.DAY_OF_MONTH, 1)
+ calendar.set(Calendar.HOUR_OF_DAY, hour)
+ calendar.set(Calendar.MINUTE, minute)
+ calendar.set(Calendar.SECOND, second)
+ calendar.set(Calendar.MILLISECOND, 0)
+
+ return calendar.timeInMillis
+ }
+
+ fun readTimezone(data: UByteArray, start: Int): Long {
+ var timezoneOffset = readTime(data, start)
+ if (data[start + 2] != 0.toUByte()) {
+ timezoneOffset *= -1
+ }
+
+ return timezoneOffset
+ }
+
+ fun readGlucose(data: UByteArray, start: Int): Int {
+ val lowBit = data[start].toInt()
+ val highBit = data[start+1].toInt() shl 8
+ return lowBit or highBit
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/util/EversenseE3Writer.kt b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/util/EversenseE3Writer.kt
new file mode 100644
index 00000000000..922d3af4acf
--- /dev/null
+++ b/plugins/eversense/src/main/kotlin/com/nightscout/eversense/packets/e3/util/EversenseE3Writer.kt
@@ -0,0 +1,77 @@
+package com.nightscout.eversense.packets.e3.util
+
+import java.util.Calendar
+import java.util.TimeZone
+import kotlin.math.PI
+
+class EversenseE3Writer {
+ companion object {
+ fun generateChecksumCRC16(data: ByteArray): ByteArray {
+ var crc = 0xFFFF
+
+ for (byte in data) {
+ var currentByte = byte.toInt() and 0xFF
+ repeat(8) {
+ val xor = ((crc shr 15) and 0x01) xor ((currentByte shr 7) and 0x01)
+ crc = (crc shl 1) and 0xFFFF
+ if (xor != 0) {
+ crc = (crc xor 0x1021) and 0xFFFF
+ }
+ currentByte = (currentByte shl 1) and 0xFF
+ }
+ }
+
+ return writeInt16(crc)
+ }
+
+ fun writeDate(timestamp: Long): ByteArray {
+ val calendar = Calendar.getInstance()
+ calendar.setTimeInMillis(timestamp)
+
+ val year = calendar.get(Calendar.YEAR) - 2000
+ val month = calendar.get(Calendar.MONTH) + 1
+ val day = calendar.get(Calendar.DAY_OF_MONTH)
+
+ val byte1 = (month shl 5) or day
+ val byte2 = (year shl 1) or (if (month >= 8) 1 else 0)
+
+ return byteArrayOf(byte1.toByte(), byte2.toByte())
+ }
+
+ fun writeTime(timestamp: Long): ByteArray {
+ val calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
+ calendar.setTimeInMillis(timestamp)
+
+ val hour = calendar.get(Calendar.HOUR_OF_DAY)
+ val minute = calendar.get(Calendar.MINUTE)
+ val second = calendar.get(Calendar.SECOND)
+
+ val byte1 = ((minute and 7) shl 5) or (second / 2)
+ val byte2 = (hour shl 3) or ((minute and 56) shr 3)
+
+ return byteArrayOf(byte1.toByte(), byte2.toByte())
+ }
+
+ fun writeTimezone(timestamp: Long): ByteArray {
+ val timezoneOffset = TimeZone.getDefault().getOffset(timestamp)
+ val timezoneNegative = if (timezoneOffset < 0) 255 else 0
+
+ return writeTime(timezoneOffset.toLong()) + byteArrayOf(timezoneNegative.toByte())
+ }
+
+ fun writeBoolean(value: Boolean): ByteArray {
+ return byteArrayOf(if (value) 0x55 else 0x00)
+ }
+
+ fun writeDouble(value: Double): ByteArray {
+ return writeInt16((value * 10).toInt())
+ }
+
+ fun writeInt16(value: Int): ByteArray {
+ return byteArrayOf(
+ value.toByte(),
+ (value shr 8).toByte()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/source/build.gradle.kts b/plugins/source/build.gradle.kts
index b8f856c326b..dadbe70dc94 100644
--- a/plugins/source/build.gradle.kts
+++ b/plugins/source/build.gradle.kts
@@ -22,6 +22,7 @@ dependencies {
implementation(project(":core:utils"))
implementation(project(":core:validators"))
implementation(project(":shared:impl"))
+ implementation(project(":plugins:eversense"))
testImplementation(libs.androidx.work.testing)
diff --git a/plugins/source/src/main/kotlin/app/aaps/plugins/source/EversensePlugin.kt b/plugins/source/src/main/kotlin/app/aaps/plugins/source/EversensePlugin.kt
new file mode 100644
index 00000000000..89a23b892c7
--- /dev/null
+++ b/plugins/source/src/main/kotlin/app/aaps/plugins/source/EversensePlugin.kt
@@ -0,0 +1,147 @@
+package app.aaps.plugins.source
+
+import android.content.Context
+import androidx.preference.EditTextPreference
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceManager
+import androidx.preference.PreferenceScreen
+import app.aaps.core.data.model.GV
+import app.aaps.core.data.model.SourceSensor
+import app.aaps.core.data.model.TrendArrow
+import app.aaps.core.data.plugin.PluginType
+import app.aaps.core.data.ue.Sources
+import app.aaps.core.interfaces.db.PersistenceLayer
+import app.aaps.core.interfaces.logging.AAPSLogger
+import app.aaps.core.interfaces.logging.LTag
+import app.aaps.core.interfaces.plugin.PluginDescription
+import app.aaps.core.interfaces.resources.ResourceHelper
+import app.aaps.core.interfaces.source.BgSource
+import app.aaps.core.keys.interfaces.Preferences
+import app.aaps.core.validators.preferences.AdaptiveStringPreference
+import com.nightscout.eversense.EversenseCGMPlugin
+import com.nightscout.eversense.callbacks.EversenseWatcher
+import com.nightscout.eversense.enums.EversenseType
+import com.nightscout.eversense.models.EversenseCGMResult
+import com.nightscout.eversense.models.EversenseState
+import kotlinx.serialization.json.Json
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import javax.inject.Inject
+
+class EversensePlugin @Inject constructor(
+ rh: ResourceHelper,
+ private val context: Context,
+ aapsLogger: AAPSLogger,
+ preferences: Preferences
+) : AbstractBgSourcePlugin(
+ PluginDescription()
+ .mainType(PluginType.BGSOURCE)
+ .fragmentClass(BGSourceFragment::class.java.name)
+ // Fix: Explicitly point to the core object R file
+ .pluginIcon(app.aaps.core.objects.R.drawable.ic_blooddrop_48)
+ .preferencesId(PluginDescription.PREFERENCE_SCREEN)
+ .pluginName(R.string.source_eversense)
+ .preferencesVisibleInSimpleMode(false)
+ .description(R.string.description_source_eversense),
+ ownPreferences = emptyList(),
+ aapsLogger, rh, preferences
+), BgSource, EversenseWatcher {
+
+ @Inject lateinit var persistenceLayer: PersistenceLayer
+
+ private val dateFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
+
+ private var connectedPreference: Preference? = null
+ private var batteryPreference: Preference? = null
+ private var insertionPreference: Preference? = null
+ private var lastSyncPreference: Preference? = null
+
+ // No extra overrides needed; the abstract class handles defaults.
+ init {
+ EversenseCGMPlugin.instance.setContext(context, true)
+ EversenseCGMPlugin.instance.addWatcher(this)
+
+ EversenseCGMPlugin.instance.connect(null)
+ }
+
+ override fun addPreferenceScreen(preferenceManager: PreferenceManager, parent: PreferenceScreen, context: Context, requiredKey: String?) {
+ super.addPreferenceScreen(preferenceManager, parent, context, requiredKey)
+
+ val state = EversenseCGMPlugin.instance.getCurrentState() ?:run { return }
+
+ val category = PreferenceCategory(context)
+ parent.addPreference(category)
+ category.apply {
+ title = rh.gs(R.string.eversense_information_title)
+
+ val connected = Preference(context)
+ connected.key = "eversense_information_connected"
+ connected.title = rh.gs(R.string.eversense_information_connected)
+ connected.summary = if (EversenseCGMPlugin.instance.isConnected()) "✅" else "❌"
+ addPreference(connected)
+ connectedPreference = connected
+
+ val battery = Preference(context)
+ battery.key = "eversense_information_battery"
+ battery.title = rh.gs(R.string.eversense_information_battery)
+ battery.summary = "${state.batteryPercentage}%"
+ addPreference(battery)
+ batteryPreference = battery
+
+ val insertion = Preference(context)
+ insertion.key = "eversense_information_insertion_date"
+ insertion.title = rh.gs(R.string.eversense_information_insertion_date)
+ insertion.summary = dateFormatter.format(Date(state.insertionDate))
+ addPreference(insertion)
+ insertionPreference = insertion
+
+ val lastSync = Preference(context)
+ lastSync.key = "eversense_information_last_sync"
+ lastSync.title = rh.gs(R.string.eversense_information_last_sync)
+ lastSync.summary = dateFormatter.format(Date(state.lastSync))
+ addPreference(lastSync)
+ lastSyncPreference = lastSync
+ }
+ }
+
+ override fun onStateChanged(state: EversenseState) {
+ aapsLogger.info(LTag.BGSOURCE, "New state received: ${Json.encodeToString(state)}")
+
+ batteryPreference?.let { it.summary = "${state.batteryPercentage}%" }
+ insertionPreference?.let { it.summary = dateFormatter.format(Date(state.insertionDate)) }
+ lastSyncPreference?.let { it.summary = dateFormatter.format(Date(state.lastSync)) }
+ }
+
+ override fun onConnectionChanged(connected: Boolean) {
+ aapsLogger.info(LTag.BGSOURCE, "Connection changed - connected: $connected")
+
+ connectedPreference?.let { it.summary = if (connected) "✅" else "❌" }
+ }
+
+ override fun onCGMRead(type: EversenseType, readings: List) {
+ val glucoseValues = mutableListOf()
+
+ for (reading in readings) {
+ glucoseValues += GV(
+ timestamp = reading.datetime,
+ value = reading.glucoseInMgDl.toDouble(),
+ noise = null,
+ raw = null,
+ trendArrow = TrendArrow.fromString(reading.trend.type),
+ sourceSensor = when (type) {
+ EversenseType.EVERSENSE_365 -> SourceSensor.EVERSENSE_365
+ EversenseType.EVERSENSE_E3 -> SourceSensor.EVERSENSE_E3
+ }
+ )
+ }
+
+ val result = persistenceLayer.insertCgmSourceData(
+ Sources.Eversense,
+ glucoseValues,
+ listOf(),
+ null
+ ).blockingGet()
+ }
+}
diff --git a/plugins/source/src/main/res/values/strings.xml b/plugins/source/src/main/res/values/strings.xml
index e68863eb533..25a3c534435 100644
--- a/plugins/source/src/main/res/values/strings.xml
+++ b/plugins/source/src/main/res/values/strings.xml
@@ -25,7 +25,6 @@
Random BG
Generate random BG data (Demo mode only)
BG
-
BG source settings
Accept sensor start
Create event \"CGM Sensor Insert\" in Careportal automatically on received sensor start from BG source local broadcast
@@ -40,4 +39,12 @@
Sino App
Receive glucose data from Patched Sino App.
+
+ Eversense
+ Eversense E3/365 driver
+ Information
+ Connected
+ Battery percentage
+ Insertion date
+ Last synchronisation
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 0e256df9b68..61c70a6a539 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -19,6 +19,7 @@ include ':plugins:aps'
include ':plugins:automation'
include ':plugins:configuration'
include ':plugins:constraints'
+include ':plugins:eversense'
include ':plugins:insulin'
include ':plugins:main'
include ':plugins:sensitivity'