diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/nsclient/StoreDataForDb.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/nsclient/StoreDataForDb.kt index 15b548a380..f8bde64a97 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/nsclient/StoreDataForDb.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/nsclient/StoreDataForDb.kt @@ -43,7 +43,12 @@ interface StoreDataForDb { val nsIdDeviceStatuses: MutableList val nsIdFoods: MutableList + val deleteTreatment: MutableList + val deleteGlucoseValue: MutableList + + fun updateDeletedGlucoseValuesInDb() fun storeTreatmentsToDb() + fun updateDeletedTreatmentsInDb() fun storeGlucoseValuesToDb() fun storeFoodsToDb() fun scheduleNsIdUpdate() diff --git a/database/impl/src/main/java/info/nightscout/database/impl/AppRepository.kt b/database/impl/src/main/java/info/nightscout/database/impl/AppRepository.kt index 478aae9239..79dbe93032 100644 --- a/database/impl/src/main/java/info/nightscout/database/impl/AppRepository.kt +++ b/database/impl/src/main/java/info/nightscout/database/impl/AppRepository.kt @@ -143,8 +143,8 @@ import kotlin.math.roundToInt .subscribeOn(Schedulers.io()) //BG READINGS -- including invalid/history records - fun findBgReadingByNSIdSingle(nsId: String): Single> = - database.glucoseValueDao.findByNSIdMaybe(nsId).toWrappedSingle() + fun findBgReadingByNSId(nsId: String): GlucoseValue? = + database.glucoseValueDao.findByNSId(nsId) fun getModifiedBgReadingsDataFromId(lastId: Long): Single> = database.glucoseValueDao.getModifiedFrom(lastId) @@ -186,6 +186,9 @@ import kotlin.math.roundToInt .subscribeOn(Schedulers.io()) // TEMP TARGETS + fun findTemporaryTargetByNSId(nsId: String): TemporaryTarget? = + database.temporaryTargetDao.findByNSId(nsId) + /* * returns a Pair of the next entity to sync and the ID of the "update". * The update id might either be the entry id itself if it is a new entry - or the id @@ -253,6 +256,9 @@ import kotlin.math.roundToInt // PROFILE SWITCH + fun findProfileSwitchByNSId(nsId: String): ProfileSwitch? = + database.profileSwitchDao.findByNSId(nsId) + fun getNextSyncElementProfileSwitch(id: Long): Maybe> = database.profileSwitchDao.getNextModifiedOrNewAfter(id) .flatMap { nextIdElement -> @@ -309,6 +315,9 @@ import kotlin.math.roundToInt database.profileSwitchDao.getLastId() // EFFECTIVE PROFILE SWITCH + fun findEffectiveProfileSwitchByNSId(nsId: String): EffectiveProfileSwitch? = + database.effectiveProfileSwitchDao.findByNSId(nsId) + /* * returns a Pair of the next entity to sync and the ID of the "update". * The update id might either be the entry id itself if it is a new entry - or the id @@ -373,6 +382,9 @@ import kotlin.math.roundToInt * * It is a Maybe as there might be no next element. * */ + fun findTherapyEventByNSId(nsId: String): TherapyEvent? = + database.therapyEventDao.findByNSId(nsId) + fun getNextSyncElementTherapyEvent(id: Long): Maybe> = database.therapyEventDao.getNextModifiedOrNewAfter(id) .flatMap { nextIdElement -> @@ -431,6 +443,9 @@ import kotlin.math.roundToInt database.therapyEventDao.getLastId() // FOOD + fun findFoodByNSId(nsId: String): Food? = + database.foodDao.findByNSId(nsId) + /* * returns a Pair of the next entity to sync and the ID of the "update". * The update id might either be the entry id itself if it is a new entry - or the id @@ -465,6 +480,9 @@ import kotlin.math.roundToInt database.foodDao.getLastId() // BOLUS + fun findBolusByNSId(nsId: String): Bolus? = + database.bolusDao.findByNSId(nsId) + /* * returns a Pair of the next entity to sync and the ID of the "update". * The update id might either be the entry id itself if it is a new entry - or the id @@ -531,6 +549,9 @@ import kotlin.math.roundToInt database.bolusDao.getLastId() // CARBS + fun findCarbsByNSId(nsId: String): Carbs? = + database.carbsDao.findByNSId(nsId) + private fun expandCarbs(carbs: Carbs): List = if (carbs.duration == 0L) { listOf(carbs) @@ -646,6 +667,9 @@ import kotlin.math.roundToInt database.carbsDao.getLastId() // BOLUS CALCULATOR RESULT + fun findBolusCalculatorResultByNSId(nsId: String): BolusCalculatorResult? = + database.bolusCalculatorResultDao.findByNSId(nsId) + /* * returns a Pair of the next entity to sync and the ID of the "update". * The update id might either be the entry id itself if it is a new entry - or the id @@ -709,13 +733,16 @@ import kotlin.math.roundToInt database.deviceStatusDao.getLastId() // TEMPORARY BASAL + fun findTemporaryBasalByNSId(nsId: String): TemporaryBasal? = + database.temporaryBasalDao.findByNSId(nsId) + /* - * returns a Pair of the next entity to sync and the ID of the "update". - * The update id might either be the entry id itself if it is a new entry - or the id - * of the update ("historic") entry. The sync counter should be incremented to that id if it was synced successfully. - * - * It is a Maybe as there might be no next element. - * */ + * returns a Pair of the next entity to sync and the ID of the "update". + * The update id might either be the entry id itself if it is a new entry - or the id + * of the update ("historic") entry. The sync counter should be incremented to that id if it was synced successfully. + * + * It is a Maybe as there might be no next element. + * */ fun getNextSyncElementTemporaryBasal(id: Long): Maybe> = database.temporaryBasalDao.getNextModifiedOrNewAfter(id) @@ -773,13 +800,16 @@ import kotlin.math.roundToInt database.temporaryBasalDao.getLastId() // EXTENDED BOLUS + fun findExtendedBolusByNSId(nsId: String): ExtendedBolus? = + database.extendedBolusDao.findByNSId(nsId) + /* - * returns a Pair of the next entity to sync and the ID of the "update". - * The update id might either be the entry id itself if it is a new entry - or the id - * of the update ("historic") entry. The sync counter should be incremented to that id if it was synced successfully. - * - * It is a Maybe as there might be no next element. - * */ + * returns a Pair of the next entity to sync and the ID of the "update". + * The update id might either be the entry id itself if it is a new entry - or the id + * of the update ("historic") entry. The sync counter should be incremented to that id if it was synced successfully. + * + * It is a Maybe as there might be no next element. + * */ fun getNextSyncElementExtendedBolus(id: Long): Maybe> = database.extendedBolusDao.getNextModifiedOrNewAfter(id) @@ -844,6 +874,9 @@ import kotlin.math.roundToInt } // OFFLINE EVENT + fun findOfflineEventByNSId(nsId: String): OfflineEvent? = + database.offlineEventDao.findByNSId(nsId) + /* * returns a Pair of the next entity to sync and the ID of the "update". * The update id might either be the entry id itself if it is a new entry - or the id diff --git a/database/impl/src/main/java/info/nightscout/database/impl/daos/GlucoseValueDao.kt b/database/impl/src/main/java/info/nightscout/database/impl/daos/GlucoseValueDao.kt index 3b5cb40950..cb9f01b7ca 100644 --- a/database/impl/src/main/java/info/nightscout/database/impl/daos/GlucoseValueDao.kt +++ b/database/impl/src/main/java/info/nightscout/database/impl/daos/GlucoseValueDao.kt @@ -29,7 +29,7 @@ internal interface GlucoseValueDao : TraceableDao { fun getLastId(): Long? @Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE nightscoutId = :nsId AND referenceId IS NULL") - fun findByNSIdMaybe(nsId: String): Maybe + fun findByNSId(nsId: String): GlucoseValue? @Query("SELECT * FROM $TABLE_GLUCOSE_VALUES WHERE timestamp = :timestamp AND sourceSensor = :sourceSensor AND referenceId IS NULL") fun findByTimestampAndSensor(timestamp: Long, sourceSensor: GlucoseValue.SourceSensor): GlucoseValue? diff --git a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateBolusCalculatorResultTransaction.kt b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateBolusCalculatorResultTransaction.kt index e44afd114e..9b3580e9bb 100644 --- a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateBolusCalculatorResultTransaction.kt +++ b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateBolusCalculatorResultTransaction.kt @@ -8,10 +8,11 @@ class InvalidateBolusCalculatorResultTransaction(val id: Long) : Transaction() { + + override fun run(): TransactionResult { + val result = TransactionResult() + val effectiveProfileSwitch = database.effectiveProfileSwitchDao.findById(id) + ?: throw IllegalArgumentException("There is no such EffectiveProfileSwitch with the specified ID.") + if (effectiveProfileSwitch.isValid) { + effectiveProfileSwitch.isValid = false + database.effectiveProfileSwitchDao.updateExistingEntry(effectiveProfileSwitch) + result.invalidated.add(effectiveProfileSwitch) + } + return result + } + + class TransactionResult { + + val invalidated = mutableListOf() + } +} \ No newline at end of file diff --git a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateExtendedBolusTransaction.kt b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateExtendedBolusTransaction.kt index b262c53627..e9b4a486fe 100644 --- a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateExtendedBolusTransaction.kt +++ b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateExtendedBolusTransaction.kt @@ -8,9 +8,11 @@ class InvalidateExtendedBolusTransaction(val id: Long) : Transaction() { override fun run() { val food = database.foodDao.findById(id) ?: throw IllegalArgumentException("There is no such Food with the specified ID.") - food.isValid = false - database.foodDao.updateExistingEntry(food) + if (food.isValid) { + food.isValid = false + database.foodDao.updateExistingEntry(food) + } } } \ No newline at end of file diff --git a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateGlucoseValueTransaction.kt b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateGlucoseValueTransaction.kt index 77a6fd97ae..bfdcda05fd 100644 --- a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateGlucoseValueTransaction.kt +++ b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateGlucoseValueTransaction.kt @@ -11,9 +11,11 @@ class InvalidateGlucoseValueTransaction(val id: Long) : Transaction() { - - override fun run() : TransactionResult{ - val result = TransactionResult() - val current = database.profileSwitchDao.findByNSId(nsId) - if (current != null) { - current.isValid = false - database.profileSwitchDao.updateExistingEntry(current) - result.invalidated.add(current) - } - return result - } - - class TransactionResult { - val invalidated = mutableListOf() - } - -} \ No newline at end of file diff --git a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateOfflineEventTransaction.kt b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateOfflineEventTransaction.kt index 51cf8fc424..897acf78f3 100644 --- a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateOfflineEventTransaction.kt +++ b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateOfflineEventTransaction.kt @@ -1,10 +1,23 @@ package info.nightscout.database.impl.transactions -class InvalidateOfflineEventTransaction(val id: Long) : Transaction() { - override fun run() { +import info.nightscout.database.entities.OfflineEvent + +class InvalidateOfflineEventTransaction(val id: Long) : Transaction() { + + override fun run(): TransactionResult { + val result = TransactionResult() val offlineEvent = database.offlineEventDao.findById(id) ?: throw IllegalArgumentException("There is no such OfflineEvent with the specified ID.") - offlineEvent.isValid = false - database.offlineEventDao.updateExistingEntry(offlineEvent) + if (offlineEvent.isValid) { + offlineEvent.isValid = false + database.offlineEventDao.updateExistingEntry(offlineEvent) + result.invalidated.add(offlineEvent) + } + return result + } + + class TransactionResult { + + val invalidated = mutableListOf() } } \ No newline at end of file diff --git a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateProfileSwitchTransaction.kt b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateProfileSwitchTransaction.kt index f5089694fd..7d2bcfcc27 100644 --- a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateProfileSwitchTransaction.kt +++ b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateProfileSwitchTransaction.kt @@ -8,9 +8,11 @@ class InvalidateProfileSwitchTransaction(val id: Long) : Transaction() { - override fun run() { +import info.nightscout.database.entities.TemporaryTarget + +class InvalidateTemporaryTargetTransaction(val id: Long) : Transaction() { + + override fun run(): TransactionResult { + val result = TransactionResult() val temporaryTarget = database.temporaryTargetDao.findById(id) ?: throw IllegalArgumentException("There is no such TemporaryTarget with the specified ID.") - temporaryTarget.isValid = false - database.temporaryTargetDao.updateExistingEntry(temporaryTarget) + if (temporaryTarget.isValid) { + temporaryTarget.isValid = false + database.temporaryTargetDao.updateExistingEntry(temporaryTarget) + result.invalidated.add(temporaryTarget) + } + return result + } + + class TransactionResult { + + val invalidated = mutableListOf() } } \ No newline at end of file diff --git a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateTherapyEventTransaction.kt b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateTherapyEventTransaction.kt index 2cf2319a3a..b846312bab 100644 --- a/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateTherapyEventTransaction.kt +++ b/database/impl/src/main/java/info/nightscout/database/impl/transactions/InvalidateTherapyEventTransaction.kt @@ -8,9 +8,11 @@ class InvalidateTherapyEventTransaction(val id: Long) : Transaction = mutableListOf() override val nsIdFoods: MutableList = mutableListOf() + override val deleteTreatment: MutableList = mutableListOf() + override val deleteGlucoseValue: MutableList = mutableListOf() private val userEntries: MutableList = mutableListOf() private val inserted = HashMap() @@ -961,6 +975,157 @@ class StoreDataForDbImpl @Inject constructor( rxBus.send(EventNSClientNewLog("● DONE NSIDs", "")) } + override fun updateDeletedTreatmentsInDb() { + deleteTreatment.forEach { id -> + if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_insulin, false) || config.NSCLIENT) + repository.findBolusByNSId(id)?.let { bolus -> + repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating Bolus", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated Bolus $it") + invalidated.inc(Bolus::class.java.simpleName) + } + } + } + if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_carbs, false) || config.NSCLIENT) + repository.findCarbsByNSId(id)?.let { carb -> + repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating Carbs", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated Carbs $it") + invalidated.inc(Carbs::class.java.simpleName) + } + } + } + if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_temp_target, false) || config.NSCLIENT) + repository.findTemporaryTargetByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating TemporaryTarget", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryTarget $it") + invalidated.inc(TemporaryTarget::class.java.simpleName) + } + } + } + if (config.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) + repository.findTemporaryBasalByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateTemporaryBasalTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating TemporaryBasal", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated TemporaryBasal $it") + invalidated.inc(TemporaryBasal::class.java.simpleName) + } + } + } + if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) + repository.findEffectiveProfileSwitchByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateEffectiveProfileSwitchTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating EffectiveProfileSwitch", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated EffectiveProfileSwitch $it") + invalidated.inc(EffectiveProfileSwitch::class.java.simpleName) + } + } + } + if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_profile_switch, false) || config.NSCLIENT) + repository.findProfileSwitchByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateProfileSwitchTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") + invalidated.inc(ProfileSwitch::class.java.simpleName) + } + } + } + repository.findBolusCalculatorResultByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateBolusCalculatorResultTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating BolusCalculatorResult", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated BolusCalculatorResult $it") + invalidated.inc(BolusCalculatorResult::class.java.simpleName) + } + } + } + if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_therapy_events, false) || config.NSCLIENT) + repository.findTherapyEventByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateTherapyEventTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating TherapyEvent", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated TherapyEvent $it") + invalidated.inc(TherapyEvent::class.java.simpleName) + } + } + } + if (sp.getBoolean(info.nightscout.core.utils.R.string.key_ns_receive_offline_event, false) && config.isEngineeringMode() || config.NSCLIENT) + repository.findOfflineEventByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateOfflineEventTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating OfflineEvent", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated OfflineEvent $it") + invalidated.inc(OfflineEvent::class.java.simpleName) + } + } + } + if (config.isEngineeringMode() && sp.getBoolean(R.string.key_ns_receive_tbr_eb, false) || config.NSCLIENT) + repository.findExtendedBolusByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateExtendedBolusTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating ExtendedBolus", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated ExtendedBolus $it") + invalidated.inc(ExtendedBolus::class.java.simpleName) + } + } + } + } + sendLog("Bolus", Bolus::class.java.simpleName) + sendLog("Carbs", Carbs::class.java.simpleName) + sendLog("TemporaryTarget", TemporaryTarget::class.java.simpleName) + sendLog("TemporaryBasal", TemporaryBasal::class.java.simpleName) + sendLog("EffectiveProfileSwitch", EffectiveProfileSwitch::class.java.simpleName) + sendLog("ProfileSwitch", ProfileSwitch::class.java.simpleName) + sendLog("BolusCalculatorResult", BolusCalculatorResult::class.java.simpleName) + sendLog("TherapyEvent", TherapyEvent::class.java.simpleName) + sendLog("OfflineEvent", OfflineEvent::class.java.simpleName) + sendLog("ExtendedBolus", ExtendedBolus::class.java.simpleName) + } + + override fun updateDeletedGlucoseValuesInDb() { + deleteGlucoseValue.forEach { id -> + repository.findBgReadingByNSId(id)?.let { gv -> + repository.runTransactionForResult(InvalidateGlucoseValueTransaction(gv.id)) + .doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating GlucoseValue", it) } + .blockingGet() + .also { result -> + result.invalidated.forEach { + aapsLogger.debug(LTag.DATABASE, "Invalidated GlucoseValue $it") + invalidated.inc(GlucoseValue::class.java.simpleName) + } + } + } + } + sendLog("GlucoseValue", GlucoseValue::class.java.simpleName) + } + private fun sendLog(item: String, clazz: String) { inserted[clazz]?.let { rxBus.send(EventNSClientNewLog("◄ INSERT", "$item $it")) diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt index 6c6cc9ce99..3ca72805d7 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt @@ -461,7 +461,17 @@ class NSClientV3Plugin @Inject constructor( private val onDataDelete = Emitter.Listener { args -> val response = args[0] as JSONObject aapsLogger.debug(LTag.NSCLIENT, "onDataDelete: $response") - rxBus.send(EventNSClientNewLog("◄ WS DELETE", "${response.optString("collection")} ${response.optString("doc")}")) + val collection = response.optString("colName") ?: return@Listener + val identifier = response.optString("identifier") ?: return@Listener + rxBus.send(EventNSClientNewLog("◄ WS DELETE", "$collection $identifier")) + if (collection == "treatments") { + storeDataForDb.deleteTreatment.add(identifier) + storeDataForDb.updateDeletedTreatmentsInDb() + } + if (collection == "entries") { + storeDataForDb.deleteGlucoseValue.add(identifier) + storeDataForDb.updateDeletedGlucoseValuesInDb() + } } private val onAnnouncement = Emitter.Listener { args ->