Merge branch 'dev' into iobfix
This commit is contained in:
commit
595bf0314c
389 changed files with 36055 additions and 399 deletions
|
@ -14,7 +14,7 @@ android:
|
||||||
- extra-google-google_play_services
|
- extra-google-google_play_services
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
- yes | sdkmanager "platforms;android-28"
|
#- yes | sdkmanager "platforms;android-28"
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# Unit Test
|
# Unit Test
|
||||||
|
|
|
@ -215,6 +215,7 @@ allprojects {
|
||||||
flatDir {
|
flatDir {
|
||||||
dirs 'libs'
|
dirs 'libs'
|
||||||
}
|
}
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,6 +275,8 @@ dependencies {
|
||||||
implementation "com.jakewharton:butterknife:${butterknifeVersion}"
|
implementation "com.jakewharton:butterknife:${butterknifeVersion}"
|
||||||
annotationProcessor "com.jakewharton:butterknife-compiler:${butterknifeVersion}"
|
annotationProcessor "com.jakewharton:butterknife-compiler:${butterknifeVersion}"
|
||||||
|
|
||||||
|
implementation 'com.github.DavidProdinger:weekdays-selector:1.0.4'
|
||||||
|
|
||||||
testImplementation "junit:junit:4.12"
|
testImplementation "junit:junit:4.12"
|
||||||
testImplementation "org.json:json:20140107"
|
testImplementation "org.json:json:20140107"
|
||||||
testImplementation "org.mockito:mockito-core:2.8.47"
|
testImplementation "org.mockito:mockito-core:2.8.47"
|
||||||
|
@ -282,9 +285,17 @@ dependencies {
|
||||||
testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}"
|
testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}"
|
||||||
testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}"
|
testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}"
|
||||||
testImplementation "joda-time:joda-time:2.9.9"
|
testImplementation "joda-time:joda-time:2.9.9"
|
||||||
testImplementation "com.google.truth:truth:0.39"
|
testImplementation("com.google.truth:truth:0.39") {
|
||||||
testImplementation 'org.robolectric:robolectric:4.2.1'
|
exclude group: "com.google.guava", module: "guava"
|
||||||
|
}
|
||||||
|
testImplementation("org.robolectric:robolectric:4.2.1") {
|
||||||
|
exclude group: "com.google.guava", module: "guava"
|
||||||
|
}
|
||||||
testImplementation "org.skyscreamer:jsonassert:1.5.0"
|
testImplementation "org.skyscreamer:jsonassert:1.5.0"
|
||||||
|
testImplementation "org.hamcrest:hamcrest-all:1.3"
|
||||||
|
testImplementation("uk.org.lidalia:slf4j-test:1.2.0") {
|
||||||
|
exclude group: "com.google.guava", module: "guava"
|
||||||
|
}
|
||||||
|
|
||||||
androidTestImplementation "org.mockito:mockito-core:2.8.47"
|
androidTestImplementation "org.mockito:mockito-core:2.8.47"
|
||||||
androidTestImplementation "com.google.dexmaker:dexmaker:${dexmakerVersion}"
|
androidTestImplementation "com.google.dexmaker:dexmaker:${dexmakerVersion}"
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
|
<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="com.dexcom.cgm.EXTERNAL_PERMISSION" />
|
<uses-permission android:name="com.dexcom.cgm.EXTERNAL_PERMISSION" />
|
||||||
|
@ -161,6 +162,9 @@
|
||||||
<service
|
<service
|
||||||
android:name=".services.DataService"
|
android:name=".services.DataService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<service
|
||||||
|
android:name=".services.LocationService"
|
||||||
|
android:exported="false" />
|
||||||
<service
|
<service
|
||||||
android:name=".plugins.pump.danaR.services.DanaRExecutionService"
|
android:name=".plugins.pump.danaR.services.DanaRExecutionService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
|
@ -284,6 +288,23 @@
|
||||||
android:theme="@style/AppTheme" />
|
android:theme="@style/AppTheme" />
|
||||||
<activity android:name=".activities.RequestDexcomPermissionActivity" />
|
<activity android:name=".activities.RequestDexcomPermissionActivity" />
|
||||||
|
|
||||||
|
<!-- Medtronic service and activities -->
|
||||||
|
<service
|
||||||
|
android:name=".plugins.pump.medtronic.service.RileyLinkMedtronicService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true" />
|
||||||
|
<activity android:name=".plugins.pump.common.dialog.RileyLinkBLEScanActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="info.nightscout.androidaps.plugins.PumpCommon.dialog.RileyLinkBLEScanActivity" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".plugins.pump.common.hw.rileylink.dialog.RileyLinkStatusActivity"
|
||||||
|
android:label="@string/title_activity_rileylink_settings"
|
||||||
|
android:theme="@style/Theme.AppCompat.NoTitle" />
|
||||||
|
<activity android:name=".plugins.pump.medtronic.dialog.MedtronicHistoryActivity" />
|
||||||
|
|
||||||
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
|
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class Constants {
|
||||||
public static final long remoteBolusMinDistance = 15 * 60 * 1000L;
|
public static final long remoteBolusMinDistance = 15 * 60 * 1000L;
|
||||||
|
|
||||||
// Circadian Percentage Profile
|
// Circadian Percentage Profile
|
||||||
public static final int CPP_MIN_PERCENTAGE = 50;
|
public static final int CPP_MIN_PERCENTAGE = 30;
|
||||||
public static final int CPP_MAX_PERCENTAGE = 200;
|
public static final int CPP_MAX_PERCENTAGE = 200;
|
||||||
public static final int CPP_MIN_TIMESHIFT = -6;
|
public static final int CPP_MIN_TIMESHIFT = -6;
|
||||||
public static final int CPP_MAX_TIMESHIFT = 23;
|
public static final int CPP_MAX_TIMESHIFT = 23;
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package info.nightscout.androidaps;
|
package info.nightscout.androidaps;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.PluralsRes;
|
import androidx.annotation.PluralsRes;
|
||||||
|
@ -40,6 +45,7 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugi
|
||||||
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin;
|
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin;
|
||||||
import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintPlugin;
|
import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintPlugin;
|
||||||
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment;
|
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
|
||||||
import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin;
|
import info.nightscout.androidaps.plugins.general.careportal.CareportalPlugin;
|
||||||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin;
|
import info.nightscout.androidaps.plugins.general.food.FoodPlugin;
|
||||||
import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils;
|
import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils;
|
||||||
|
@ -63,12 +69,15 @@ import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin;
|
||||||
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin;
|
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin;
|
||||||
import info.nightscout.androidaps.plugins.profile.simple.SimpleProfilePlugin;
|
import info.nightscout.androidaps.plugins.profile.simple.SimpleProfilePlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin;
|
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
|
||||||
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin;
|
import info.nightscout.androidaps.plugins.pump.danaR.DanaRPlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin;
|
import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
|
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
|
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
|
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin;
|
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
|
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
|
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
|
||||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
|
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
|
||||||
|
@ -86,6 +95,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||||
import info.nightscout.androidaps.receivers.DataReceiver;
|
import info.nightscout.androidaps.receivers.DataReceiver;
|
||||||
import info.nightscout.androidaps.receivers.KeepAliveReceiver;
|
import info.nightscout.androidaps.receivers.KeepAliveReceiver;
|
||||||
import info.nightscout.androidaps.receivers.NSAlarmReceiver;
|
import info.nightscout.androidaps.receivers.NSAlarmReceiver;
|
||||||
|
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver;
|
||||||
import info.nightscout.androidaps.services.Intents;
|
import info.nightscout.androidaps.services.Intents;
|
||||||
import info.nightscout.androidaps.utils.FabricPrivacy;
|
import info.nightscout.androidaps.utils.FabricPrivacy;
|
||||||
import io.fabric.sdk.android.Fabric;
|
import io.fabric.sdk.android.Fabric;
|
||||||
|
@ -113,6 +123,8 @@ public class MainApp extends Application {
|
||||||
private static AckAlarmReceiver ackAlarmReciever = new AckAlarmReceiver();
|
private static AckAlarmReceiver ackAlarmReciever = new AckAlarmReceiver();
|
||||||
private static DBAccessReceiver dbAccessReciever = new DBAccessReceiver();
|
private static DBAccessReceiver dbAccessReciever = new DBAccessReceiver();
|
||||||
private LocalBroadcastManager lbm;
|
private LocalBroadcastManager lbm;
|
||||||
|
BroadcastReceiver btReceiver;
|
||||||
|
TimeDateOrTZChangeReceiver timeDateOrTZChangeReceiver;
|
||||||
|
|
||||||
public static boolean devBranch;
|
public static boolean devBranch;
|
||||||
public static boolean engineeringMode;
|
public static boolean engineeringMode;
|
||||||
|
@ -154,6 +166,7 @@ public class MainApp extends Application {
|
||||||
|
|
||||||
//trigger here to see the new version on app start after an update
|
//trigger here to see the new version on app start after an update
|
||||||
triggerCheckVersion();
|
triggerCheckVersion();
|
||||||
|
//setBTReceiver();
|
||||||
|
|
||||||
if (pluginsList == null) {
|
if (pluginsList == null) {
|
||||||
pluginsList = new ArrayList<>();
|
pluginsList = new ArrayList<>();
|
||||||
|
@ -175,6 +188,7 @@ public class MainApp extends Application {
|
||||||
if (Config.PUMPDRIVERS) pluginsList.add(LocalInsightPlugin.getPlugin());
|
if (Config.PUMPDRIVERS) pluginsList.add(LocalInsightPlugin.getPlugin());
|
||||||
pluginsList.add(CareportalPlugin.getPlugin());
|
pluginsList.add(CareportalPlugin.getPlugin());
|
||||||
if (Config.PUMPDRIVERS) pluginsList.add(ComboPlugin.getPlugin());
|
if (Config.PUMPDRIVERS) pluginsList.add(ComboPlugin.getPlugin());
|
||||||
|
if (Config.PUMPDRIVERS && engineeringMode) pluginsList.add(MedtronicPumpPlugin.getPlugin());
|
||||||
if (Config.MDI) pluginsList.add(MDIPlugin.getPlugin());
|
if (Config.MDI) pluginsList.add(MDIPlugin.getPlugin());
|
||||||
pluginsList.add(VirtualPumpPlugin.getPlugin());
|
pluginsList.add(VirtualPumpPlugin.getPlugin());
|
||||||
if (Config.APS) pluginsList.add(LoopPlugin.getPlugin());
|
if (Config.APS) pluginsList.add(LoopPlugin.getPlugin());
|
||||||
|
@ -207,6 +221,8 @@ public class MainApp extends Application {
|
||||||
if (engineeringMode)
|
if (engineeringMode)
|
||||||
pluginsList.add(TidepoolPlugin.INSTANCE);
|
pluginsList.add(TidepoolPlugin.INSTANCE);
|
||||||
pluginsList.add(MaintenancePlugin.initPlugin(this));
|
pluginsList.add(MaintenancePlugin.initPlugin(this));
|
||||||
|
if (engineeringMode)
|
||||||
|
pluginsList.add(AutomationPlugin.INSTANCE);
|
||||||
|
|
||||||
pluginsList.add(ConfigBuilderPlugin.getPlugin());
|
pluginsList.add(ConfigBuilderPlugin.getPlugin());
|
||||||
|
|
||||||
|
@ -254,6 +270,10 @@ public class MainApp extends Application {
|
||||||
|
|
||||||
//register dbaccess
|
//register dbaccess
|
||||||
lbm.registerReceiver(dbAccessReciever, new IntentFilter(Intents.ACTION_DATABASE));
|
lbm.registerReceiver(dbAccessReciever, new IntentFilter(Intents.ACTION_DATABASE));
|
||||||
|
|
||||||
|
this.timeDateOrTZChangeReceiver = new TimeDateOrTZChangeReceiver();
|
||||||
|
this.timeDateOrTZChangeReceiver.registerBroadcasts(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startKeepAliveService() {
|
private void startKeepAliveService() {
|
||||||
|
@ -439,5 +459,19 @@ public class MainApp extends Application {
|
||||||
sDatabaseHelper.close();
|
sDatabaseHelper.close();
|
||||||
sDatabaseHelper = null;
|
sDatabaseHelper = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (btReceiver != null) {
|
||||||
|
unregisterReceiver(btReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeDateOrTZChangeReceiver!=null) {
|
||||||
|
unregisterReceiver(timeDateOrTZChangeReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int dpToPx(int dp) {
|
||||||
|
float scale = sResources.getDisplayMetrics().density;
|
||||||
|
return (int) (dp * scale + 0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import info.nightscout.androidaps.plugins.pump.danaRKorean.DanaRKoreanPlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
|
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
|
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
|
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
|
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
|
||||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
|
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
|
||||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
|
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin;
|
||||||
|
@ -48,6 +49,7 @@ import info.nightscout.androidaps.plugins.source.SourceDexcomPlugin;
|
||||||
import info.nightscout.androidaps.utils.LocaleHelper;
|
import info.nightscout.androidaps.utils.LocaleHelper;
|
||||||
import info.nightscout.androidaps.utils.OKDialog;
|
import info.nightscout.androidaps.utils.OKDialog;
|
||||||
import info.nightscout.androidaps.utils.SP;
|
import info.nightscout.androidaps.utils.SP;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
|
||||||
|
|
||||||
public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
MyPreferenceFragment myPreferenceFragment;
|
MyPreferenceFragment myPreferenceFragment;
|
||||||
|
@ -170,6 +172,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
|
||||||
addPreferencesFromResourceIfEnabled(DanaRSPlugin.getPlugin(), PluginType.PUMP);
|
addPreferencesFromResourceIfEnabled(DanaRSPlugin.getPlugin(), PluginType.PUMP);
|
||||||
addPreferencesFromResourceIfEnabled(LocalInsightPlugin.getPlugin(), PluginType.PUMP);
|
addPreferencesFromResourceIfEnabled(LocalInsightPlugin.getPlugin(), PluginType.PUMP);
|
||||||
addPreferencesFromResourceIfEnabled(ComboPlugin.getPlugin(), PluginType.PUMP);
|
addPreferencesFromResourceIfEnabled(ComboPlugin.getPlugin(), PluginType.PUMP);
|
||||||
|
addPreferencesFromResourceIfEnabled(MedtronicPumpPlugin.getPlugin(), PluginType.PUMP);
|
||||||
|
|
||||||
if (DanaRPlugin.getPlugin().isEnabled(PluginType.PROFILE)
|
if (DanaRPlugin.getPlugin().isEnabled(PluginType.PROFILE)
|
||||||
|| DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PROFILE)
|
|| DanaRKoreanPlugin.getPlugin().isEnabled(PluginType.PROFILE)
|
||||||
|
@ -188,6 +191,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
|
||||||
addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL);
|
addPreferencesFromResourceIfEnabled(NSClientPlugin.getPlugin(), PluginType.GENERAL);
|
||||||
addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL);
|
addPreferencesFromResourceIfEnabled(TidepoolPlugin.INSTANCE, PluginType.GENERAL);
|
||||||
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL);
|
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL);
|
||||||
|
addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL);
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.pref_others);
|
addPreferencesFromResource(R.xml.pref_others);
|
||||||
addPreferencesFromResource(R.xml.pref_datachoices);
|
addPreferencesFromResource(R.xml.pref_datachoices);
|
||||||
|
|
|
@ -45,6 +45,11 @@ public class PumpEnactResult {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PumpEnactResult comment(int comment) {
|
||||||
|
this.comment = MainApp.gs(comment);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public PumpEnactResult duration(int duration) {
|
public PumpEnactResult duration(int duration) {
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -21,6 +21,8 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
@ -536,6 +538,24 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
||||||
return tddList;
|
return tddList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TDD> getTDDsForLastXDays(int days) {
|
||||||
|
List<TDD> tddList;
|
||||||
|
GregorianCalendar gc = new GregorianCalendar();
|
||||||
|
gc.add(Calendar.DAY_OF_YEAR, (-1) * days);
|
||||||
|
|
||||||
|
try {
|
||||||
|
QueryBuilder<TDD, String> queryBuilder = getDaoTDD().queryBuilder();
|
||||||
|
queryBuilder.orderBy("date", false);
|
||||||
|
Where<TDD, String> where = queryBuilder.where();
|
||||||
|
where.ge("date", gc.getTimeInMillis());
|
||||||
|
PreparedQuery<TDD> preparedQuery = queryBuilder.prepare();
|
||||||
|
tddList = getDaoTDD().query(preparedQuery);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.error("Unhandled exception", e);
|
||||||
|
tddList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
return tddList;
|
||||||
|
}
|
||||||
|
|
||||||
// ------------- DbRequests handling -------------------
|
// ------------- DbRequests handling -------------------
|
||||||
|
|
||||||
|
@ -871,6 +891,31 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
||||||
log.debug("TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
|
log.debug("TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// search by date (in case its standard record that has become pump record)
|
||||||
|
QueryBuilder<TemporaryBasal, Long> queryBuilder2 = getDaoTemporaryBasal().queryBuilder();
|
||||||
|
Where where2 = queryBuilder2.where();
|
||||||
|
where2.eq("date", tempBasal.date);
|
||||||
|
PreparedQuery<TemporaryBasal> preparedQuery2 = queryBuilder2.prepare();
|
||||||
|
List<TemporaryBasal> trList2 = getDaoTemporaryBasal().query(preparedQuery2);
|
||||||
|
|
||||||
|
if (trList2.size() > 0) {
|
||||||
|
old = trList2.get(0);
|
||||||
|
|
||||||
|
old.copyFromPump(tempBasal);
|
||||||
|
old.source = Source.PUMP;
|
||||||
|
|
||||||
|
if (L.isEnabled(L.DATABASE))
|
||||||
|
log.debug("TEMPBASAL: Updated record with Pump Data : " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
|
||||||
|
|
||||||
|
getDaoTemporaryBasal().update(old);
|
||||||
|
|
||||||
|
updateEarliestDataChange(tempBasal.date);
|
||||||
|
scheduleTemporaryBasalChange();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
getDaoTemporaryBasal().create(tempBasal);
|
getDaoTemporaryBasal().create(tempBasal);
|
||||||
if (L.isEnabled(L.DATABASE))
|
if (L.isEnabled(L.DATABASE))
|
||||||
log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
|
log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
|
||||||
|
@ -1111,6 +1156,29 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TemporaryBasal findTempBasalByPumpId(Long pumpId) {
|
||||||
|
try {
|
||||||
|
QueryBuilder<TemporaryBasal, Long> queryBuilder = null;
|
||||||
|
queryBuilder = getDaoTemporaryBasal().queryBuilder();
|
||||||
|
queryBuilder.orderBy("date", false);
|
||||||
|
Where where = queryBuilder.where();
|
||||||
|
where.eq("pumpId", pumpId);
|
||||||
|
PreparedQuery<TemporaryBasal> preparedQuery = queryBuilder.prepare();
|
||||||
|
List<TemporaryBasal> list = getDaoTemporaryBasal().query(preparedQuery);
|
||||||
|
|
||||||
|
if (list.size() > 0)
|
||||||
|
return list.get(0);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.error("Unhandled exception", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------ ExtendedBolus handling ---------------
|
// ------------ ExtendedBolus handling ---------------
|
||||||
|
|
||||||
public boolean createOrUpdate(ExtendedBolus extendedBolus) {
|
public boolean createOrUpdate(ExtendedBolus extendedBolus) {
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package info.nightscout.androidaps.db;
|
||||||
|
|
||||||
|
public interface DbObjectBase {
|
||||||
|
|
||||||
|
long getDate();
|
||||||
|
|
||||||
|
long getPumpId();
|
||||||
|
|
||||||
|
}
|
|
@ -6,9 +6,8 @@ import com.j256.ormlite.table.DatabaseTable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.logging.L;
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mike on 20.09.2017.
|
* Created by mike on 20.09.2017.
|
||||||
|
@ -45,4 +44,16 @@ public class TDD {
|
||||||
this.basal = basal;
|
this.basal = basal;
|
||||||
this.total = total;
|
this.total = total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TDD [" +
|
||||||
|
"date=" + date +
|
||||||
|
"date(str)=" + DateTimeUtil.toStringFromTimeInMillis(date) +
|
||||||
|
", bolus=" + bolus +
|
||||||
|
", basal=" + basal +
|
||||||
|
", total=" + total +
|
||||||
|
']';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@ import org.slf4j.LoggerFactory;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import info.nightscout.androidaps.Constants;
|
import info.nightscout.androidaps.Constants;
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
import info.nightscout.androidaps.interfaces.Interval;
|
import info.nightscout.androidaps.interfaces.Interval;
|
||||||
import info.nightscout.androidaps.logging.L;
|
import info.nightscout.androidaps.logging.L;
|
||||||
import info.nightscout.androidaps.utils.DateUtil;
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
@ -191,4 +194,11 @@ public class TempTarget implements Interval {
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String friendlyDescription(String units) {
|
||||||
|
return Profile.toTargetRangeString(low, high, Constants.MGDL, units) +
|
||||||
|
units +
|
||||||
|
"@" + MainApp.gs(R.string.mins, durationInMinutes) +
|
||||||
|
(reason != null && !reason.equals("") ? "(" + reason + ")" : "");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import info.nightscout.androidaps.utils.SP;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@DatabaseTable(tableName = DatabaseHelper.DATABASE_TEMPORARYBASALS)
|
@DatabaseTable(tableName = DatabaseHelper.DATABASE_TEMPORARYBASALS)
|
||||||
public class TemporaryBasal implements Interval {
|
public class TemporaryBasal implements Interval, DbObjectBase {
|
||||||
private static Logger log = LoggerFactory.getLogger(L.DATABASE);
|
private static Logger log = LoggerFactory.getLogger(L.DATABASE);
|
||||||
|
|
||||||
@DatabaseField(id = true)
|
@DatabaseField(id = true)
|
||||||
|
@ -157,6 +157,14 @@ public class TemporaryBasal implements Interval {
|
||||||
netExtendedRate = t.netExtendedRate;
|
netExtendedRate = t.netExtendedRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void copyFromPump(TemporaryBasal t) {
|
||||||
|
durationInMinutes = t.durationInMinutes;
|
||||||
|
isAbsolute = t.isAbsolute;
|
||||||
|
percentRate = t.percentRate;
|
||||||
|
absoluteRate = t.absoluteRate;
|
||||||
|
pumpId = t.pumpId;
|
||||||
|
}
|
||||||
|
|
||||||
// -------- Interval interface ---------
|
// -------- Interval interface ---------
|
||||||
|
|
||||||
Long cuttedEnd = null;
|
Long cuttedEnd = null;
|
||||||
|
@ -490,4 +498,13 @@ public class TemporaryBasal implements Interval {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDate() {
|
||||||
|
return this.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getPumpId() {
|
||||||
|
return this.pumpId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
package info.nightscout.androidaps.events;
|
||||||
|
|
||||||
|
public class EventCustomActionsChanged extends Event {
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package info.nightscout.androidaps.events;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
|
||||||
|
public class EventLocationChange extends Event {
|
||||||
|
public Location location;
|
||||||
|
|
||||||
|
public EventLocationChange(Location location) {
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
package info.nightscout.androidaps.interfaces;
|
package info.nightscout.androidaps.interfaces;
|
||||||
|
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.utils.SP;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -42,6 +45,28 @@ public abstract class PluginBase {
|
||||||
pluginSwitcher.invoke();
|
pluginSwitcher.invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void confirmPumpPluginActivation(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity activity) {
|
||||||
|
boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false);
|
||||||
|
if (allowHardwarePump || activity == null) {
|
||||||
|
pluginSwitcher.invoke();
|
||||||
|
} else {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
|
builder.setMessage(R.string.allow_hardware_pump_text)
|
||||||
|
.setPositiveButton(R.string.yes, (dialog, id) -> {
|
||||||
|
pluginSwitcher.invoke();
|
||||||
|
SP.putBoolean("allow_hardware_pump", true);
|
||||||
|
if (L.isEnabled(L.PUMP))
|
||||||
|
log.debug("First time HW pump allowed!");
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, (dialog, id) -> {
|
||||||
|
pluginSwitcher.cancel();
|
||||||
|
if (L.isEnabled(L.PUMP))
|
||||||
|
log.debug("User does not allow switching to HW pump!");
|
||||||
|
});
|
||||||
|
builder.create().show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// public PluginType getType() {
|
// public PluginType getType() {
|
||||||
// return mainType;
|
// return mainType;
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -7,8 +7,10 @@ import java.util.List;
|
||||||
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||||
import info.nightscout.androidaps.data.Profile;
|
import info.nightscout.androidaps.data.Profile;
|
||||||
import info.nightscout.androidaps.data.PumpEnactResult;
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.plugins.common.ManufacturerType;
|
||||||
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction;
|
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction;
|
||||||
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType;
|
import info.nightscout.androidaps.plugins.general.actions.defs.CustomActionType;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by mike on 04.06.2016.
|
* Created by mike on 04.06.2016.
|
||||||
|
@ -53,8 +55,8 @@ public interface PumpInterface {
|
||||||
|
|
||||||
// Status to be passed to NS
|
// Status to be passed to NS
|
||||||
JSONObject getJSONStatus(Profile profile, String profileName);
|
JSONObject getJSONStatus(Profile profile, String profileName);
|
||||||
String manufacter();
|
ManufacturerType manufacturer();
|
||||||
String model();
|
PumpType model();
|
||||||
String serialNumber();
|
String serialNumber();
|
||||||
|
|
||||||
// Pump capabilities
|
// Pump capabilities
|
||||||
|
@ -73,4 +75,10 @@ public interface PumpInterface {
|
||||||
|
|
||||||
void executeCustomAction(CustomActionType customActionType);
|
void executeCustomAction(CustomActionType customActionType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will be called when time or Timezone changes, and pump driver can then do a specific action (for
|
||||||
|
* example update clock on pump).
|
||||||
|
*/
|
||||||
|
void timeDateOrTimeZoneChanged();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ public interface TreatmentsInterface {
|
||||||
|
|
||||||
List<Treatment> getTreatmentsFromHistory();
|
List<Treatment> getTreatmentsFromHistory();
|
||||||
List<Treatment> getTreatments5MinBackFromHistory(long time);
|
List<Treatment> getTreatments5MinBackFromHistory(long time);
|
||||||
|
List<Treatment> getTreatmentsFromHistoryAfterTimestamp(long timestamp);
|
||||||
long getLastBolusTime();
|
long getLastBolusTime();
|
||||||
|
|
||||||
// real basals (not faked by extended bolus)
|
// real basals (not faked by extended bolus)
|
||||||
|
|
|
@ -77,6 +77,7 @@ public class L {
|
||||||
|
|
||||||
public static final String CORE = "CORE";
|
public static final String CORE = "CORE";
|
||||||
public static final String AUTOSENS = "AUTOSENS";
|
public static final String AUTOSENS = "AUTOSENS";
|
||||||
|
public static final String AUTOMATION = "AUTOMATION";
|
||||||
public static final String EVENTS = "EVENTS";
|
public static final String EVENTS = "EVENTS";
|
||||||
public static final String GLUCOSE = "GLUCOSE";
|
public static final String GLUCOSE = "GLUCOSE";
|
||||||
public static final String BGSOURCE = "BGSOURCE";
|
public static final String BGSOURCE = "BGSOURCE";
|
||||||
|
@ -97,11 +98,13 @@ public class L {
|
||||||
public static final String PROFILE = "PROFILE";
|
public static final String PROFILE = "PROFILE";
|
||||||
public static final String CONFIGBUILDER = "CONFIGBUILDER";
|
public static final String CONFIGBUILDER = "CONFIGBUILDER";
|
||||||
public static final String UI = "UI";
|
public static final String UI = "UI";
|
||||||
|
public static final String LOCATION = "LOCATION";
|
||||||
public static final String SMS = "SMS";
|
public static final String SMS = "SMS";
|
||||||
|
|
||||||
private static void initialize() {
|
private static void initialize() {
|
||||||
logElements = new ArrayList<>();
|
logElements = new ArrayList<>();
|
||||||
logElements.add(new LogElement(APS, true));
|
logElements.add(new LogElement(APS, true));
|
||||||
|
logElements.add(new LogElement(AUTOMATION, true));
|
||||||
logElements.add(new LogElement(AUTOSENS, false));
|
logElements.add(new LogElement(AUTOSENS, false));
|
||||||
logElements.add(new LogElement(BGSOURCE, true));
|
logElements.add(new LogElement(BGSOURCE, true));
|
||||||
logElements.add(new LogElement(GLUCOSE, false));
|
logElements.add(new LogElement(GLUCOSE, false));
|
||||||
|
@ -113,6 +116,7 @@ public class L {
|
||||||
logElements.add(new LogElement(DATASERVICE, true));
|
logElements.add(new LogElement(DATASERVICE, true));
|
||||||
logElements.add(new LogElement(DATATREATMENTS, true));
|
logElements.add(new LogElement(DATATREATMENTS, true));
|
||||||
logElements.add(new LogElement(EVENTS, false, true));
|
logElements.add(new LogElement(EVENTS, false, true));
|
||||||
|
logElements.add(new LogElement(LOCATION, true));
|
||||||
logElements.add(new LogElement(NOTIFICATION, true));
|
logElements.add(new LogElement(NOTIFICATION, true));
|
||||||
logElements.add(new LogElement(NSCLIENT, true));
|
logElements.add(new LogElement(NSCLIENT, true));
|
||||||
logElements.add(new LogElement(TIDEPOOL, true));
|
logElements.add(new LogElement(TIDEPOOL, true));
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package info.nightscout.androidaps.plugins.common;
|
||||||
|
|
||||||
|
public enum ManufacturerType {
|
||||||
|
|
||||||
|
AndroidAPS("AndroidAPS"),
|
||||||
|
Medtronic("Medtronic"),
|
||||||
|
Sooil("SOOIL"),
|
||||||
|
|
||||||
|
Tandem("Tandem"),
|
||||||
|
Insulet("Insulet"),
|
||||||
|
Animas("Animas"), Cellnovo("Cellnovo"), Roche("Roche");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
ManufacturerType(String description) {
|
||||||
|
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import info.nightscout.androidaps.activities.HistoryBrowseActivity;
|
||||||
import info.nightscout.androidaps.activities.TDDStatsActivity;
|
import info.nightscout.androidaps.activities.TDDStatsActivity;
|
||||||
import info.nightscout.androidaps.db.ExtendedBolus;
|
import info.nightscout.androidaps.db.ExtendedBolus;
|
||||||
import info.nightscout.androidaps.db.TemporaryBasal;
|
import info.nightscout.androidaps.db.TemporaryBasal;
|
||||||
|
import info.nightscout.androidaps.events.EventCustomActionsChanged;
|
||||||
import info.nightscout.androidaps.events.EventExtendedBolusChange;
|
import info.nightscout.androidaps.events.EventExtendedBolusChange;
|
||||||
import info.nightscout.androidaps.events.EventInitializationChanged;
|
import info.nightscout.androidaps.events.EventInitializationChanged;
|
||||||
import info.nightscout.androidaps.events.EventRefreshOverview;
|
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||||
|
@ -133,6 +134,11 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
|
||||||
updateGUI();
|
updateGUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onStatusEvent(final EventCustomActionsChanged ev) {
|
||||||
|
updateGUI();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void updateGUI() {
|
protected void updateGUI() {
|
||||||
Activity activity = getActivity();
|
Activity activity = getActivity();
|
||||||
|
@ -247,6 +253,9 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
|
||||||
|
|
||||||
for (CustomAction customAction : customActions) {
|
for (CustomAction customAction : customActions) {
|
||||||
|
|
||||||
|
if (!customAction.isEnabled())
|
||||||
|
continue;
|
||||||
|
|
||||||
SingleClickButton btn = new SingleClickButton(getContext(), null, android.R.attr.buttonStyle);
|
SingleClickButton btn = new SingleClickButton(getContext(), null, android.R.attr.buttonStyle);
|
||||||
btn.setText(MainApp.gs(customAction.getName()));
|
btn.setText(MainApp.gs(customAction.getName()));
|
||||||
|
|
||||||
|
@ -264,10 +273,8 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
|
||||||
|
|
||||||
this.pumpCustomActions.put(MainApp.gs(customAction.getName()), customAction);
|
this.pumpCustomActions.put(MainApp.gs(customAction.getName()), customAction);
|
||||||
this.pumpCustomButtons.add(btn);
|
this.pumpCustomButtons.add(btn);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -283,7 +290,6 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
|
||||||
}
|
}
|
||||||
|
|
||||||
pumpCustomButtons.clear();
|
pumpCustomButtons.clear();
|
||||||
pumpCustomActions.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,31 +12,36 @@ public class CustomAction {
|
||||||
private String iconName;
|
private String iconName;
|
||||||
private CustomActionType customActionType;
|
private CustomActionType customActionType;
|
||||||
private int iconResourceId;
|
private int iconResourceId;
|
||||||
|
private boolean enabled = true;
|
||||||
|
|
||||||
|
|
||||||
public CustomAction(int nameResourceId, CustomActionType actionType) {
|
public CustomAction(int nameResourceId, CustomActionType actionType) {
|
||||||
this.name = nameResourceId;
|
this(nameResourceId, actionType, R.drawable.icon_actions_profileswitch, true);
|
||||||
this.customActionType = actionType;
|
|
||||||
this.iconResourceId = R.drawable.icon_actions_profileswitch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CustomAction(int nameResourceId, CustomActionType actionType, int iconResourceId) {
|
public CustomAction(int nameResourceId, CustomActionType actionType, int iconResourceId) {
|
||||||
|
this(nameResourceId, actionType, iconResourceId, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomAction(int nameResourceId, CustomActionType actionType, boolean enabled) {
|
||||||
|
this(nameResourceId, actionType, R.drawable.icon_actions_profileswitch, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomAction(int nameResourceId, CustomActionType actionType, int iconResourceId, boolean enabled) {
|
||||||
this.name = nameResourceId;
|
this.name = nameResourceId;
|
||||||
this.customActionType = actionType;
|
this.customActionType = actionType;
|
||||||
this.iconResourceId = iconResourceId;
|
this.iconResourceId = iconResourceId;
|
||||||
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int getName() {
|
public int getName() {
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public CustomActionType getCustomActionType() {
|
public CustomActionType getCustomActionType() {
|
||||||
|
|
||||||
return customActionType;
|
return customActionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,4 +49,15 @@ public class CustomAction {
|
||||||
public int getIconResourceId() {
|
public int getIconResourceId() {
|
||||||
return iconResourceId;
|
return iconResourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ public class FillDialog extends DialogFragment implements OnClickListener {
|
||||||
Double maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
Double maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
||||||
double bolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep;
|
double bolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep;
|
||||||
editInsulin = view.findViewById(R.id.fill_insulinamount);
|
editInsulin = view.findViewById(R.id.fill_insulinamount);
|
||||||
editInsulin.setParams(0d, 0d, maxInsulin, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false, textWatcher);
|
editInsulin.setParams(0d, 0d, maxInsulin, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
|
|
||||||
|
|
||||||
Button preset1Button = view.findViewById(R.id.fill_preset_button1);
|
Button preset1Button = view.findViewById(R.id.fill_preset_button1);
|
||||||
|
@ -184,7 +184,7 @@ public class FillDialog extends DialogFragment implements OnClickListener {
|
||||||
confirmMessage.add("");
|
confirmMessage.add("");
|
||||||
confirmMessage.add(MainApp.gs(R.string.bolus) + ": " + "<font color='" + MainApp.gc(R.color.colorCarbsButton) + "'>" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + "</font>");
|
confirmMessage.add(MainApp.gs(R.string.bolus) + ": " + "<font color='" + MainApp.gc(R.color.colorCarbsButton) + "'>" + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints) + "U" + "</font>");
|
||||||
if (Math.abs(insulinAfterConstraints - insulin) > 0.01d)
|
if (Math.abs(insulinAfterConstraints - insulin) > 0.01d)
|
||||||
confirmMessage.add("<font color='" + MainApp.gc(R.color.low) + "'>" + MainApp.gs(R.string.bolusconstraintapplied) + "</font>");
|
confirmMessage.add(MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pumpSiteChangeCheckbox.isChecked())
|
if (pumpSiteChangeCheckbox.isChecked())
|
||||||
|
|
|
@ -42,12 +42,12 @@ public class NewExtendedBolusDialog extends DialogFragment implements View.OnCli
|
||||||
|
|
||||||
Double maxInsulin = MainApp.getConstraintChecker().getMaxExtendedBolusAllowed().value();
|
Double maxInsulin = MainApp.getConstraintChecker().getMaxExtendedBolusAllowed().value();
|
||||||
editInsulin = (NumberPicker) view.findViewById(R.id.overview_newextendedbolus_insulin);
|
editInsulin = (NumberPicker) view.findViewById(R.id.overview_newextendedbolus_insulin);
|
||||||
editInsulin.setParams(0d, 0d, maxInsulin, 0.1d, new DecimalFormat("0.00"), false);
|
editInsulin.setParams(0d, 0d, maxInsulin, 0.1d, new DecimalFormat("0.00"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
double extendedDurationStep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().extendedBolusDurationStep;
|
double extendedDurationStep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().extendedBolusDurationStep;
|
||||||
double extendedMaxDuration = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().extendedBolusMaxDuration;
|
double extendedMaxDuration = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().extendedBolusMaxDuration;
|
||||||
editDuration = (NumberPicker) view.findViewById(R.id.overview_newextendedbolus_duration);
|
editDuration = (NumberPicker) view.findViewById(R.id.overview_newextendedbolus_duration);
|
||||||
editDuration.setParams(extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, new DecimalFormat("0"), false);
|
editDuration.setParams(extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
view.findViewById(R.id.ok).setOnClickListener(this);
|
view.findViewById(R.id.ok).setOnClickListener(this);
|
||||||
view.findViewById(R.id.cancel).setOnClickListener(this);
|
view.findViewById(R.id.cancel).setOnClickListener(this);
|
||||||
|
|
|
@ -66,17 +66,17 @@ public class NewTempBasalDialog extends DialogFragment implements View.OnClickLi
|
||||||
basalPercent = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_basalpercentinput);
|
basalPercent = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_basalpercentinput);
|
||||||
double maxTempPercent = pumpDescription.maxTempPercent;
|
double maxTempPercent = pumpDescription.maxTempPercent;
|
||||||
double tempPercentStep = pumpDescription.tempPercentStep;
|
double tempPercentStep = pumpDescription.tempPercentStep;
|
||||||
basalPercent.setParams(100d, 0d, maxTempPercent, tempPercentStep, new DecimalFormat("0"), true);
|
basalPercent.setParams(100d, 0d, maxTempPercent, tempPercentStep, new DecimalFormat("0"), true, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
Profile profile = ProfileFunctions.getInstance().getProfile();
|
Profile profile = ProfileFunctions.getInstance().getProfile();
|
||||||
Double currentBasal = profile != null ? profile.getBasal() : 0d;
|
Double currentBasal = profile != null ? profile.getBasal() : 0d;
|
||||||
basalAbsolute = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_basalabsoluteinput);
|
basalAbsolute = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_basalabsoluteinput);
|
||||||
basalAbsolute.setParams(currentBasal, 0d, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, new DecimalFormat("0.00"), true);
|
basalAbsolute.setParams(currentBasal, 0d, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, new DecimalFormat("0.00"), true, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
double tempDurationStep = pumpDescription.tempDurationStep;
|
double tempDurationStep = pumpDescription.tempDurationStep;
|
||||||
double tempMaxDuration = pumpDescription.tempMaxDuration;
|
double tempMaxDuration = pumpDescription.tempMaxDuration;
|
||||||
duration = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_duration);
|
duration = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_duration);
|
||||||
duration.setParams(tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, new DecimalFormat("0"), false);
|
duration.setParams(tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
if ((pumpDescription.tempBasalStyle & PumpDescription.PERCENT) == PumpDescription.PERCENT && (pumpDescription.tempBasalStyle & PumpDescription.ABSOLUTE) == PumpDescription.ABSOLUTE) {
|
if ((pumpDescription.tempBasalStyle & PumpDescription.PERCENT) == PumpDescription.PERCENT && (pumpDescription.tempBasalStyle & PumpDescription.ABSOLUTE) == PumpDescription.ABSOLUTE) {
|
||||||
// Both allowed
|
// Both allowed
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
|
||||||
|
|
||||||
|
public class AutomationEvent {
|
||||||
|
|
||||||
|
private Trigger trigger = new TriggerConnector();
|
||||||
|
private List<Action> actions = new ArrayList<>();
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrigger(Trigger trigger) {
|
||||||
|
this.trigger = trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Trigger getTrigger() {
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Action> getActions() {
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerConnector getPreconditions() {
|
||||||
|
TriggerConnector trigger = new TriggerConnector(TriggerConnector.Type.AND);
|
||||||
|
for (Action action : actions) {
|
||||||
|
if (action.precondition != null)
|
||||||
|
trigger.add(action.precondition);
|
||||||
|
}
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAction(Action action) {
|
||||||
|
actions.add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
// title
|
||||||
|
o.put("title", title);
|
||||||
|
// trigger
|
||||||
|
o.put("trigger", trigger.toJSON());
|
||||||
|
// actions
|
||||||
|
JSONArray array = new JSONArray();
|
||||||
|
for (Action a : actions) {
|
||||||
|
array.put(a.toJSON());
|
||||||
|
}
|
||||||
|
o.put("actions", array);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public AutomationEvent fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
// title
|
||||||
|
title = d.optString("title", "");
|
||||||
|
// trigger
|
||||||
|
trigger = Trigger.instantiate(d.getString("trigger"));
|
||||||
|
// actions
|
||||||
|
JSONArray array = d.getJSONArray("actions");
|
||||||
|
actions.clear();
|
||||||
|
for (int i = 0; i < array.length(); i++) {
|
||||||
|
actions.add(Action.instantiate(new JSONObject(array.getString(i))));
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.disposables.Disposable
|
||||||
|
import kotlinx.android.synthetic.main.automation_fragment.*
|
||||||
|
|
||||||
|
class AutomationFragment : Fragment() {
|
||||||
|
|
||||||
|
private var disposable: CompositeDisposable = CompositeDisposable()
|
||||||
|
private var eventListAdapter: EventListAdapter? = null
|
||||||
|
|
||||||
|
operator fun CompositeDisposable.plusAssign(disposable: Disposable) {
|
||||||
|
add(disposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.automation_fragment, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
eventListAdapter = EventListAdapter(AutomationPlugin.automationEvents, fragmentManager)
|
||||||
|
automation_eventListView.layoutManager = LinearLayoutManager(context)
|
||||||
|
automation_eventListView.adapter = eventListAdapter
|
||||||
|
|
||||||
|
automation_fabAddEvent.setOnClickListener {
|
||||||
|
val dialog = EditEventDialog()
|
||||||
|
val args = Bundle()
|
||||||
|
args.putString("event", AutomationEvent().toJSON())
|
||||||
|
args.putInt("position", -1) // New event
|
||||||
|
dialog.arguments = args
|
||||||
|
fragmentManager?.let { dialog.show(it, "EditEventDialog") }
|
||||||
|
}
|
||||||
|
|
||||||
|
disposable += RxBus
|
||||||
|
.toObservable(EventAutomationUpdateGui::class.java)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({
|
||||||
|
eventListAdapter?.notifyDataSetChanged()
|
||||||
|
val sb = StringBuilder()
|
||||||
|
for (l in AutomationPlugin.executionLog) {
|
||||||
|
sb.append(l)
|
||||||
|
sb.append("\n")
|
||||||
|
}
|
||||||
|
automation_logView.text = sb.toString()
|
||||||
|
}, {})
|
||||||
|
disposable += RxBus
|
||||||
|
.toObservable(EventAutomationDataChanged::class.java)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({
|
||||||
|
eventListAdapter?.notifyDataSetChanged()
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
disposable.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,213 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Handler
|
||||||
|
import info.nightscout.androidaps.MainApp
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.events.EventChargingState
|
||||||
|
import info.nightscout.androidaps.events.EventLocationChange
|
||||||
|
import info.nightscout.androidaps.events.EventNetworkChange
|
||||||
|
import info.nightscout.androidaps.events.EventPreferenceChange
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginBase
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginType
|
||||||
|
import info.nightscout.androidaps.logging.L
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.actions.*
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationDataChanged
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.*
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
|
||||||
|
import info.nightscout.androidaps.queue.Callback
|
||||||
|
import info.nightscout.androidaps.services.LocationService
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.SP
|
||||||
|
import info.nightscout.androidaps.utils.T
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import io.reactivex.disposables.Disposable
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object AutomationPlugin : PluginBase(PluginDescription()
|
||||||
|
.mainType(PluginType.GENERAL)
|
||||||
|
.fragmentClass(AutomationFragment::class.qualifiedName)
|
||||||
|
.pluginName(R.string.automation)
|
||||||
|
.shortName(R.string.automation_short)
|
||||||
|
.preferencesId(R.xml.pref_automation)
|
||||||
|
.description(R.string.automation_description)) {
|
||||||
|
|
||||||
|
private val log = LoggerFactory.getLogger(L.AUTOMATION)
|
||||||
|
private var disposable: CompositeDisposable = CompositeDisposable()
|
||||||
|
|
||||||
|
private const val key_AUTOMATION_EVENTS = "AUTOMATION_EVENTS"
|
||||||
|
|
||||||
|
val automationEvents = ArrayList<AutomationEvent>()
|
||||||
|
var executionLog: MutableList<String> = ArrayList()
|
||||||
|
|
||||||
|
private val loopHandler = Handler()
|
||||||
|
private lateinit var refreshLoop: Runnable
|
||||||
|
|
||||||
|
init {
|
||||||
|
refreshLoop = Runnable {
|
||||||
|
processActions()
|
||||||
|
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun CompositeDisposable.plusAssign(disposable: Disposable) {
|
||||||
|
add(disposable)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
val context = MainApp.instance().applicationContext
|
||||||
|
context.startService(Intent(context, LocationService::class.java))
|
||||||
|
|
||||||
|
super.onStart()
|
||||||
|
loadFromSP()
|
||||||
|
loopHandler.postDelayed(refreshLoop, T.mins(1).msecs())
|
||||||
|
|
||||||
|
disposable += RxBus
|
||||||
|
.toObservable(EventPreferenceChange::class.java)
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.subscribe({ e ->
|
||||||
|
if (e.isChanged(R.string.key_location)) {
|
||||||
|
val ctx = MainApp.instance().applicationContext
|
||||||
|
ctx.stopService(Intent(ctx, LocationService::class.java))
|
||||||
|
ctx.startService(Intent(ctx, LocationService::class.java))
|
||||||
|
}
|
||||||
|
}, {})
|
||||||
|
disposable += RxBus
|
||||||
|
.toObservable(EventAutomationDataChanged::class.java)
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.subscribe({ storeToSP() }, {})
|
||||||
|
disposable += RxBus
|
||||||
|
.toObservable(EventLocationChange::class.java)
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.subscribe({ e ->
|
||||||
|
e?.let {
|
||||||
|
log.debug("Grabbed location: $it.location.latitude $it.location.longitude Provider: $it.location.provider")
|
||||||
|
processActions()
|
||||||
|
}
|
||||||
|
}, {})
|
||||||
|
disposable += RxBus
|
||||||
|
.toObservable(EventChargingState::class.java)
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.subscribe({ processActions() }, {})
|
||||||
|
disposable += RxBus
|
||||||
|
.toObservable(EventNetworkChange::class.java)
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.subscribe({ processActions() }, {})
|
||||||
|
disposable += RxBus
|
||||||
|
.toObservable(EventAutosensCalculationFinished::class.java)
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.subscribe({ processActions() }, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
disposable.clear()
|
||||||
|
loopHandler.removeCallbacks(refreshLoop)
|
||||||
|
val context = MainApp.instance().applicationContext
|
||||||
|
context.stopService(Intent(context, LocationService::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun storeToSP() {
|
||||||
|
val array = JSONArray()
|
||||||
|
try {
|
||||||
|
for (event in automationEvents) {
|
||||||
|
array.put(JSONObject(event.toJSON()))
|
||||||
|
}
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
SP.putString(key_AUTOMATION_EVENTS, array.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadFromSP() {
|
||||||
|
automationEvents.clear()
|
||||||
|
val data = SP.getString(key_AUTOMATION_EVENTS, "")
|
||||||
|
if (data != "") {
|
||||||
|
try {
|
||||||
|
val array = JSONArray(data)
|
||||||
|
for (i in 0 until array.length()) {
|
||||||
|
val o = array.getJSONObject(i)
|
||||||
|
val event = AutomationEvent().fromJSON(o.toString())
|
||||||
|
automationEvents.add(event)
|
||||||
|
}
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun processActions() {
|
||||||
|
if (!isEnabled(PluginType.GENERAL))
|
||||||
|
return
|
||||||
|
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("processActions")
|
||||||
|
for (event in automationEvents) {
|
||||||
|
if (event.trigger.shouldRun() && event.preconditions.shouldRun()) {
|
||||||
|
val actions = event.actions
|
||||||
|
for (action in actions) {
|
||||||
|
action.doAction(object : Callback() {
|
||||||
|
override fun run() {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
sb.append(DateUtil.timeString(DateUtil.now()))
|
||||||
|
sb.append(" ")
|
||||||
|
sb.append(if (result.success) "☺" else "X")
|
||||||
|
sb.append(" ")
|
||||||
|
sb.append(event.title)
|
||||||
|
sb.append(": ")
|
||||||
|
sb.append(action.shortDescription())
|
||||||
|
sb.append(": ")
|
||||||
|
sb.append(result.comment)
|
||||||
|
executionLog.add(sb.toString())
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Executed: $sb")
|
||||||
|
RxBus.send(EventAutomationUpdateGui())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
event.trigger.executed(DateUtil.now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
storeToSP() // save last run time
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getActionDummyObjects(): List<Action> {
|
||||||
|
return listOf(
|
||||||
|
//ActionLoopDisable(),
|
||||||
|
//ActionLoopEnable(),
|
||||||
|
//ActionLoopResume(),
|
||||||
|
//ActionLoopSuspend(),
|
||||||
|
ActionStartTempTarget(),
|
||||||
|
ActionStopTempTarget(),
|
||||||
|
ActionNotification(),
|
||||||
|
ActionProfileSwitchPercent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTriggerDummyObjects(): List<Trigger> {
|
||||||
|
return listOf(
|
||||||
|
TriggerTime(),
|
||||||
|
TriggerRecurringTime(),
|
||||||
|
TriggerBg(),
|
||||||
|
TriggerDelta(),
|
||||||
|
TriggerIob(),
|
||||||
|
TriggerCOB(),
|
||||||
|
TriggerProfilePercent(),
|
||||||
|
TriggerTempTarget(),
|
||||||
|
TriggerWifiSsid(),
|
||||||
|
TriggerLocation(),
|
||||||
|
TriggerAutosensValue(),
|
||||||
|
TriggerBolusAgo()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.actions.Action;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
|
||||||
|
|
||||||
|
class EventListAdapter extends RecyclerView.Adapter<EventListAdapter.ViewHolder> {
|
||||||
|
private final List<AutomationEvent> mEventList;
|
||||||
|
private final FragmentManager mFragmentManager;
|
||||||
|
|
||||||
|
EventListAdapter(List<AutomationEvent> events, FragmentManager fragmentManager) {
|
||||||
|
this.mEventList = events;
|
||||||
|
this.mFragmentManager = fragmentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.automation_event_item, parent, false);
|
||||||
|
return new ViewHolder(v, parent.getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addImage(@DrawableRes int res, Context context, LinearLayout layout) {
|
||||||
|
ImageView iv = new ImageView(context);
|
||||||
|
iv.setImageResource(res);
|
||||||
|
iv.setLayoutParams(new LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)));
|
||||||
|
layout.addView(iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
final AutomationEvent event = mEventList.get(position);
|
||||||
|
holder.eventTitle.setText(event.getTitle());
|
||||||
|
holder.iconLayout.removeAllViews();
|
||||||
|
|
||||||
|
// trigger icons
|
||||||
|
HashSet<Integer> triggerIcons = new HashSet<>();
|
||||||
|
TriggerConnector.fillIconSet((TriggerConnector) event.getTrigger(), triggerIcons);
|
||||||
|
for (int res : triggerIcons) {
|
||||||
|
addImage(res, holder.context, holder.iconLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// arrow icon
|
||||||
|
ImageView iv = new ImageView(holder.context);
|
||||||
|
iv.setImageResource(R.drawable.ic_arrow_forward_white_24dp);
|
||||||
|
iv.setLayoutParams(new LinearLayout.LayoutParams(MainApp.dpToPx(24), MainApp.dpToPx(24)));
|
||||||
|
iv.setPadding(MainApp.dpToPx(4), 0, MainApp.dpToPx(4), 0);
|
||||||
|
holder.iconLayout.addView(iv);
|
||||||
|
|
||||||
|
// action icons
|
||||||
|
HashSet<Integer> actionIcons = new HashSet<>();
|
||||||
|
for (Action action : event.getActions()) {
|
||||||
|
if (action.icon().isPresent())
|
||||||
|
actionIcons.add(action.icon().get());
|
||||||
|
}
|
||||||
|
for (int res : actionIcons) {
|
||||||
|
addImage(res, holder.context, holder.iconLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove event
|
||||||
|
holder.iconTrash.setOnClickListener(v -> {
|
||||||
|
mEventList.remove(event);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
// edit event
|
||||||
|
holder.rootLayout.setOnClickListener(v -> {
|
||||||
|
//EditEventDialog dialog = EditEventDialog.Companion.newInstance(event, false);
|
||||||
|
EditEventDialog dialog = new EditEventDialog();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putString("event", event.toJSON());
|
||||||
|
args.putInt("position", position);
|
||||||
|
dialog.setArguments(args);
|
||||||
|
if (mFragmentManager != null)
|
||||||
|
dialog.show(mFragmentManager, "EditEventDialog");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return mEventList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
final RelativeLayout rootLayout;
|
||||||
|
final LinearLayout iconLayout;
|
||||||
|
final TextView eventTitle;
|
||||||
|
final Context context;
|
||||||
|
final ImageView iconTrash;
|
||||||
|
|
||||||
|
ViewHolder(View view, Context context) {
|
||||||
|
super(view);
|
||||||
|
this.context = context;
|
||||||
|
eventTitle = view.findViewById(R.id.viewEventTitle);
|
||||||
|
rootLayout = view.findViewById(R.id.rootLayout);
|
||||||
|
iconLayout = view.findViewById(R.id.iconLayout);
|
||||||
|
iconTrash = view.findViewById(R.id.iconTrash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Action ideas:
|
||||||
|
|
||||||
|
* cancel temp target
|
||||||
|
* change preference setting
|
||||||
|
* enable/disable plugin
|
||||||
|
* create notification
|
||||||
|
* create ugly alarm
|
||||||
|
* create profile switch
|
||||||
|
* set/cancel tbr
|
||||||
|
* set/cancel extended bolus
|
||||||
|
* run bolus wizard
|
||||||
|
|
||||||
|
Trigger ideas:
|
||||||
|
|
||||||
|
* location (close to)
|
||||||
|
* connected to specific wifi
|
||||||
|
* internet available/not available
|
||||||
|
* nsclient connected/disconnected
|
||||||
|
* iob
|
||||||
|
* cob
|
||||||
|
* autosens value
|
||||||
|
* delta, short delta, long delta
|
||||||
|
* last bolus ago
|
||||||
|
* is tbr running
|
||||||
|
* bolus wizard result
|
||||||
|
* loop is enabled, disabled, suspended, running
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class Action {
|
||||||
|
|
||||||
|
public Trigger precondition = null;
|
||||||
|
|
||||||
|
public abstract int friendlyName();
|
||||||
|
|
||||||
|
public abstract String shortDescription();
|
||||||
|
|
||||||
|
public abstract void doAction(Callback callback);
|
||||||
|
|
||||||
|
public void generateDialog(LinearLayout root) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDialog() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", this.getClass().getName());
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Optional<Integer> icon();
|
||||||
|
|
||||||
|
public Action fromJSON(String data) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Action instantiate(JSONObject object) {
|
||||||
|
try {
|
||||||
|
String type = object.getString("type");
|
||||||
|
JSONObject data = object.optJSONObject("data");
|
||||||
|
Class clazz = Class.forName(type);
|
||||||
|
return ((Action) clazz.newInstance()).fromJSON(data != null ? data.toString() : "");
|
||||||
|
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(Action a) {
|
||||||
|
try {
|
||||||
|
JSONObject object = new JSONObject(a.toJSON());
|
||||||
|
String type = object.getString("type");
|
||||||
|
JSONObject data = object.getJSONObject("data");
|
||||||
|
if (type.equals(getClass().getName())) {
|
||||||
|
fromJSON(data.toString());
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginType;
|
||||||
|
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
|
||||||
|
public class ActionLoopDisable extends Action {
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.disableloop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortDescription() {
|
||||||
|
return MainApp.gs(R.string.disableloop);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(Callback callback) {
|
||||||
|
if (LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
|
||||||
|
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, false);
|
||||||
|
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopDisable");
|
||||||
|
ConfigBuilderPlugin.getPlugin().getCommandQueue().cancelTempBasal(true, new Callback() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopDisable"));
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(result).run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadydisabled)).run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_stop_24dp);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginType;
|
||||||
|
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
|
||||||
|
public class ActionLoopEnable extends Action {
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.enableloop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortDescription() {
|
||||||
|
return MainApp.gs(R.string.enableloop);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(Callback callback) {
|
||||||
|
if (!LoopPlugin.getPlugin().isEnabled(PluginType.LOOP)) {
|
||||||
|
LoopPlugin.getPlugin().setPluginEnabled(PluginType.LOOP, true);
|
||||||
|
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopEnable");
|
||||||
|
RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopEnable"));
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||||
|
} else {
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadyenabled)).run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_play_circle_outline_24dp);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||||
|
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
|
||||||
|
public class ActionLoopResume extends Action {
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.resumeloop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortDescription() {
|
||||||
|
return MainApp.gs(R.string.resumeloop);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(Callback callback) {
|
||||||
|
if (LoopPlugin.getPlugin().isSuspended()) {
|
||||||
|
LoopPlugin.getPlugin().suspendTo(0);
|
||||||
|
ConfigBuilderPlugin.getPlugin().storeSettings("ActionLoopResume");
|
||||||
|
NSUpload.uploadOpenAPSOffline(0);
|
||||||
|
RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopResume"));
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||||
|
} else {
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.notsuspended)).run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_replay_24dp);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||||
|
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
|
||||||
|
public class ActionLoopSuspend extends Action {
|
||||||
|
public InputDuration minutes = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.suspendloop;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortDescription() {
|
||||||
|
return MainApp.gs(R.string.suspendloopforXmin, minutes.getMinutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(Callback callback) {
|
||||||
|
if (!LoopPlugin.getPlugin().isSuspended()) {
|
||||||
|
LoopPlugin.getPlugin().suspendLoop(minutes.getMinutes());
|
||||||
|
RxBus.INSTANCE.send(new EventRefreshOverview("ActionLoopSuspend"));
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||||
|
} else {
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.alreadysuspended)).run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_pause_circle_outline_24dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
try {
|
||||||
|
data.put("minutes", minutes.getMinutes());
|
||||||
|
o.put("type", this.getClass().getName());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject o = new JSONObject(data);
|
||||||
|
minutes.setMinutes(JsonHelper.safeGetInt(o, "minutes"));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDialog() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root) {
|
||||||
|
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", minutes))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputString;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
|
||||||
|
public class ActionNotification extends Action {
|
||||||
|
public InputString text = new InputString();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortDescription() {
|
||||||
|
return MainApp.gs(R.string.notification_message, text.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(Callback callback) {
|
||||||
|
Notification notification = new Notification(Notification.USERMESSAGE, text.getValue(), Notification.URGENT);
|
||||||
|
RxBus.INSTANCE.send(new EventNewNotification(notification));
|
||||||
|
NSUpload.uploadError(text.getValue());
|
||||||
|
RxBus.INSTANCE.send(new EventRefreshOverview("ActionNotification"));
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_notifications);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
try {
|
||||||
|
data.put("text", text.getValue());
|
||||||
|
o.put("type", this.getClass().getName());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject o = new JSONObject(data);
|
||||||
|
text.setValue(JsonHelper.safeGetString(o, "text"));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDialog() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root) {
|
||||||
|
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.message_short), "", text))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputPercent;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerProfilePercent;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
|
||||||
|
public class ActionProfileSwitchPercent extends Action {
|
||||||
|
InputPercent pct = new InputPercent();
|
||||||
|
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
|
||||||
|
|
||||||
|
public ActionProfileSwitchPercent() {
|
||||||
|
precondition = new TriggerProfilePercent().comparator(Comparator.Compare.IS_EQUAL).setValue(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.profilepercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortDescription() {
|
||||||
|
if (duration.getMinutes() == 0)
|
||||||
|
return MainApp.gs(R.string.startprofileforever, (int) pct.getValue());
|
||||||
|
else
|
||||||
|
return MainApp.gs(R.string.startprofile, (int) pct.getValue(), duration.getMinutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(Callback callback) {
|
||||||
|
ProfileFunctions.doProfileSwitch((int) duration.getValue(), (int) pct.getValue(), 0);
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.percent_u), "", pct))
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", duration))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDialog() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", ActionProfileSwitchPercent.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("percentage", pct.getValue());
|
||||||
|
data.put("durationInMinutes", duration.getMinutes());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
pct.setValue(JsonHelper.safeGetInt(d, "percentage"));
|
||||||
|
duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes"));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.icon_actions_profileswitch);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.Constants;
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.db.Source;
|
||||||
|
import info.nightscout.androidaps.db.TempTarget;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.ComparatorExists;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputBg;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTempTarget;
|
||||||
|
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
|
||||||
|
public class ActionStartTempTarget extends Action {
|
||||||
|
String reason = "";
|
||||||
|
InputBg value = new InputBg();
|
||||||
|
InputDuration duration = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
|
||||||
|
private TempTarget tempTarget;
|
||||||
|
|
||||||
|
public ActionStartTempTarget() {
|
||||||
|
precondition = new TriggerTempTarget().comparator(ComparatorExists.Compare.NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.starttemptarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortDescription() {
|
||||||
|
tempTarget = new TempTarget()
|
||||||
|
.date(DateUtil.now())
|
||||||
|
.duration(duration.getMinutes())
|
||||||
|
.reason(reason)
|
||||||
|
.source(Source.USER)
|
||||||
|
.low(Profile.toMgdl(value.getValue(), value.getUnits()))
|
||||||
|
.high(Profile.toMgdl(value.getValue(), value.getUnits()));
|
||||||
|
return MainApp.gs(R.string.starttemptarget) + ": " + (tempTarget == null ? "null" : tempTarget.friendlyDescription(value.getUnits()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(Callback callback) {
|
||||||
|
tempTarget = new TempTarget()
|
||||||
|
.date(DateUtil.now())
|
||||||
|
.duration(duration.getMinutes())
|
||||||
|
.reason(reason)
|
||||||
|
.source(Source.USER)
|
||||||
|
.low(Profile.toMgdl(value.getValue(), value.getUnits()))
|
||||||
|
.high(Profile.toMgdl(value.getValue(), value.getUnits()));
|
||||||
|
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root) {
|
||||||
|
int unitResId = value.getUnits().equals(Constants.MGDL) ? R.string.mgdl : R.string.mmol;
|
||||||
|
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.careportal_temporarytarget), MainApp.gs(unitResId), value))
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.careportal_newnstreatment_duration_min_label), "", duration))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDialog() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", ActionStartTempTarget.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("reason", reason);
|
||||||
|
data.put("value", value.getValue());
|
||||||
|
data.put("units", value.getUnits());
|
||||||
|
data.put("durationInMinutes", duration.getMinutes());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
reason = JsonHelper.safeGetString(d, "reason");
|
||||||
|
value.setUnits(JsonHelper.safeGetString(d, "units"));
|
||||||
|
value.setValue(JsonHelper.safeGetDouble(d, "value"));
|
||||||
|
duration.setMinutes(JsonHelper.safeGetInt(d, "durationInMinutes"));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.icon_cp_cgm_target);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.actions;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.db.Source;
|
||||||
|
import info.nightscout.androidaps.db.TempTarget;
|
||||||
|
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||||
|
import info.nightscout.androidaps.queue.Callback;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
|
||||||
|
public class ActionStopTempTarget extends Action {
|
||||||
|
String reason = "";
|
||||||
|
private TempTarget tempTarget;
|
||||||
|
|
||||||
|
public ActionStopTempTarget() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.stoptemptarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String shortDescription() {
|
||||||
|
return MainApp.gs(R.string.stoptemptarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAction(Callback callback) {
|
||||||
|
tempTarget = new TempTarget().date(DateUtil.now()).duration(0).reason(reason).source(Source.USER).low(0).high(0);
|
||||||
|
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
|
||||||
|
if (callback != null)
|
||||||
|
callback.result(new PumpEnactResult().success(true).comment(R.string.ok)).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDialog() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", ActionStopTempTarget.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("reason", reason);
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
reason = JsonHelper.safeGetString(d, "reason");
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_stop_24dp);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.dialogs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.actions.Action
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
|
||||||
|
|
||||||
|
class ActionListAdapter(private val fragmentManager: FragmentManager, private val actionList: MutableList<Action>) : RecyclerView.Adapter<ActionListAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val v = LayoutInflater.from(parent.context).inflate(R.layout.automation_action_item, parent, false)
|
||||||
|
return ViewHolder(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val action = actionList[position]
|
||||||
|
holder.bind(action, fragmentManager, this, position, actionList)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return actionList.size
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
|
||||||
|
fun bind(action: Action, fragmentManager: FragmentManager, recyclerView: RecyclerView.Adapter<ViewHolder>, position : Int, actionList: MutableList<Action>) {
|
||||||
|
view.findViewById<LinearLayout>(R.id.automation_layoutText).setOnClickListener {
|
||||||
|
if (action.hasDialog()) {
|
||||||
|
val args = Bundle()
|
||||||
|
args.putInt("actionPosition", position)
|
||||||
|
args.putString("action", action.toJSON())
|
||||||
|
val dialog = EditActionDialog()
|
||||||
|
dialog.arguments = args
|
||||||
|
dialog.show(fragmentManager, "EditActionDialog")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view.findViewById<ImageView>(R.id.automation_iconTrash).setOnClickListener {
|
||||||
|
actionList.remove(action)
|
||||||
|
recyclerView.notifyDataSetChanged()
|
||||||
|
RxBus.send(EventAutomationUpdateGui())
|
||||||
|
}
|
||||||
|
view.findViewById<TextView>(R.id.automation_viewActionTitle).text = action.shortDescription()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.dialogs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.RadioButton
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.actions.Action
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationAddAction
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
|
||||||
|
import kotlinx.android.synthetic.main.automation_dialog_choose_action.*
|
||||||
|
import kotlinx.android.synthetic.main.okcancel.*
|
||||||
|
|
||||||
|
class ChooseActionDialog : DialogFragment() {
|
||||||
|
|
||||||
|
var checkedIndex = -1
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?): View? {
|
||||||
|
// restore checked radio button
|
||||||
|
savedInstanceState?.let { bundle ->
|
||||||
|
checkedIndex = bundle.getInt("checkedIndex")
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
return inflater.inflate(R.layout.automation_dialog_choose_action, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
for (a in AutomationPlugin.getActionDummyObjects()) {
|
||||||
|
val radioButton = RadioButton(context)
|
||||||
|
radioButton.setText(a.friendlyName())
|
||||||
|
radioButton.tag = a.javaClass
|
||||||
|
automation_radioGroup.addView(radioButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkedIndex != -1)
|
||||||
|
(automation_radioGroup.getChildAt(checkedIndex) as RadioButton).isChecked = true
|
||||||
|
|
||||||
|
// OK button
|
||||||
|
ok.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
instantiateAction()?.let {
|
||||||
|
RxBus.send(EventAutomationAddAction(it))
|
||||||
|
RxBus.send(EventAutomationUpdateGui())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel button
|
||||||
|
cancel.setOnClickListener { dismiss() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(bundle: Bundle) {
|
||||||
|
bundle.putInt("checkedIndex", determineCheckedIndex())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun instantiateAction(): Action? {
|
||||||
|
return getActionClass()?.let {
|
||||||
|
it.newInstance() as Action
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getActionClass(): Class<*>? {
|
||||||
|
val radioButtonID = automation_radioGroup.checkedRadioButtonId
|
||||||
|
val radioButton = automation_radioGroup.findViewById<RadioButton>(radioButtonID)
|
||||||
|
return radioButton?.let {
|
||||||
|
it.tag as Class<*>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun determineCheckedIndex(): Int {
|
||||||
|
for (i in 0 until automation_radioGroup.childCount) {
|
||||||
|
if ((automation_radioGroup.getChildAt(i) as RadioButton).isChecked)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.dialogs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.RadioButton
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
|
||||||
|
import kotlinx.android.synthetic.main.automation_dialog_choose_trigger.*
|
||||||
|
import kotlinx.android.synthetic.main.okcancel.*
|
||||||
|
|
||||||
|
class ChooseTriggerDialog : DialogFragment() {
|
||||||
|
|
||||||
|
private var checkedIndex = -1
|
||||||
|
|
||||||
|
private var clickListener: OnClickListener? = null
|
||||||
|
|
||||||
|
interface OnClickListener {
|
||||||
|
fun onClick(newTriggerObject: Trigger?)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?): View? {
|
||||||
|
// restore checked radio button
|
||||||
|
savedInstanceState?.let { bundle ->
|
||||||
|
checkedIndex = bundle.getInt("checkedIndex")
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
return inflater.inflate(R.layout.automation_dialog_choose_trigger, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
for (t in AutomationPlugin.getTriggerDummyObjects()) {
|
||||||
|
val radioButton = RadioButton(context)
|
||||||
|
radioButton.setText(t.friendlyName())
|
||||||
|
radioButton.tag = t.javaClass
|
||||||
|
automation_chooseTriggerRadioGroup.addView(radioButton)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkedIndex != -1)
|
||||||
|
(automation_chooseTriggerRadioGroup.getChildAt(checkedIndex) as RadioButton).isChecked = true
|
||||||
|
|
||||||
|
// OK button
|
||||||
|
ok.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
clickListener?.onClick(instantiateTrigger())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel button
|
||||||
|
cancel.setOnClickListener { dismiss() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnClickListener(clickListener: OnClickListener) {
|
||||||
|
this.clickListener = clickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(bundle: Bundle) {
|
||||||
|
bundle.putInt("checkedIndex", determineCheckedIndex())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun instantiateTrigger(): Trigger? {
|
||||||
|
return getTriggerClass()?.let {
|
||||||
|
it.newInstance() as Trigger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTriggerClass(): Class<*>? {
|
||||||
|
val radioButtonID = automation_chooseTriggerRadioGroup.checkedRadioButtonId
|
||||||
|
val radioButton = automation_chooseTriggerRadioGroup.findViewById<RadioButton>(radioButtonID)
|
||||||
|
return radioButton?.let {
|
||||||
|
it.tag as Class<*>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun determineCheckedIndex(): Int {
|
||||||
|
for (i in 0 until automation_chooseTriggerRadioGroup.childCount) {
|
||||||
|
if ((automation_chooseTriggerRadioGroup.getChildAt(i) as RadioButton).isChecked)
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.dialogs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Button
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.actions.Action
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateAction
|
||||||
|
import kotlinx.android.synthetic.main.automation_dialog_action.*
|
||||||
|
import kotlinx.android.synthetic.main.okcancel.*
|
||||||
|
import org.json.JSONObject
|
||||||
|
|
||||||
|
class EditActionDialog : DialogFragment() {
|
||||||
|
|
||||||
|
private var action: Action? = null
|
||||||
|
private var actionPosition: Int = -1
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?): View? {
|
||||||
|
// load data from bundle
|
||||||
|
(savedInstanceState ?: arguments)?.let { bundle ->
|
||||||
|
actionPosition = bundle.getInt("actionPosition", -1)
|
||||||
|
bundle.getString("action")?.let { action = Action.instantiate(JSONObject(it)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
return inflater.inflate(R.layout.automation_dialog_action, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
action?.let {
|
||||||
|
automation_actionTitle.setText(it.friendlyName())
|
||||||
|
automation_editActionLayout.removeAllViews()
|
||||||
|
it.generateDialog(automation_editActionLayout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK button
|
||||||
|
ok.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
action?.let {
|
||||||
|
RxBus.send(EventAutomationUpdateAction(it, actionPosition))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel button
|
||||||
|
cancel.setOnClickListener { dismiss() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(bundle: Bundle) {
|
||||||
|
super.onSaveInstanceState(bundle)
|
||||||
|
action?.let {
|
||||||
|
bundle.putInt("actionPosition", actionPosition)
|
||||||
|
bundle.putString("action", it.toJSON())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.dialogs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.AutomationEvent
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.*
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
|
||||||
|
import info.nightscout.androidaps.utils.ToastUtils
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
|
import kotlinx.android.synthetic.main.automation_dialog_event.*
|
||||||
|
import kotlinx.android.synthetic.main.okcancel.*
|
||||||
|
|
||||||
|
class EditEventDialog : DialogFragment() {
|
||||||
|
|
||||||
|
private var actionListAdapter: ActionListAdapter? = null
|
||||||
|
private var event: AutomationEvent = AutomationEvent()
|
||||||
|
private var position: Int = -1
|
||||||
|
|
||||||
|
private var disposable: CompositeDisposable = CompositeDisposable()
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?): View? {
|
||||||
|
// load data from bundle
|
||||||
|
(savedInstanceState ?: arguments)?.let { bundle ->
|
||||||
|
position = bundle.getInt("position", -1)
|
||||||
|
bundle.getString("event")?.let { event = AutomationEvent().fromJSON(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
return inflater.inflate(R.layout.automation_dialog_event, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
automation_inputEventTitle.setText(event.title)
|
||||||
|
automation_triggerDescription.text = event.trigger.friendlyDescription()
|
||||||
|
|
||||||
|
automation_editTrigger.setOnClickListener {
|
||||||
|
val args = Bundle()
|
||||||
|
args.putString("trigger", event.trigger.toJSON())
|
||||||
|
val dialog = EditTriggerDialog()
|
||||||
|
dialog.arguments = args
|
||||||
|
fragmentManager?.let { dialog.show(it, "EditTriggerDialog") }
|
||||||
|
}
|
||||||
|
|
||||||
|
// setup action list view
|
||||||
|
fragmentManager?.let { actionListAdapter = ActionListAdapter(it, event.actions) }
|
||||||
|
automation_actionListView.layoutManager = LinearLayoutManager(context)
|
||||||
|
automation_actionListView.adapter = actionListAdapter
|
||||||
|
|
||||||
|
automation_addAction.setOnClickListener { fragmentManager?.let { ChooseActionDialog().show(it, "ChooseActionDialog") } }
|
||||||
|
|
||||||
|
// OK button
|
||||||
|
ok.setOnClickListener {
|
||||||
|
// check for title
|
||||||
|
val title = automation_inputEventTitle.text.toString()
|
||||||
|
if (title.isEmpty()) {
|
||||||
|
ToastUtils.showToastInUiThread(context, R.string.automation_missing_task_name)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
event.title = title
|
||||||
|
// check for at least one trigger
|
||||||
|
val con = event.trigger as TriggerConnector
|
||||||
|
if (con.size() == 0) {
|
||||||
|
ToastUtils.showToastInUiThread(context, R.string.automation_missing_trigger)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
// check for at least one action
|
||||||
|
if (event.actions.isEmpty()) {
|
||||||
|
ToastUtils.showToastInUiThread(context, R.string.automation_missing_action)
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
// store
|
||||||
|
if (position == -1)
|
||||||
|
AutomationPlugin.automationEvents.add(event)
|
||||||
|
else
|
||||||
|
AutomationPlugin.automationEvents[position] = event
|
||||||
|
|
||||||
|
dismiss()
|
||||||
|
RxBus.send(EventAutomationDataChanged())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel button
|
||||||
|
cancel.setOnClickListener { dismiss() }
|
||||||
|
|
||||||
|
showPreconditions()
|
||||||
|
|
||||||
|
disposable.add(RxBus
|
||||||
|
.toObservable(EventAutomationUpdateGui::class.java)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({
|
||||||
|
actionListAdapter?.notifyDataSetChanged()
|
||||||
|
showPreconditions()
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
disposable.add(RxBus
|
||||||
|
.toObservable(EventAutomationAddAction::class.java)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({
|
||||||
|
event.addAction(it.action)
|
||||||
|
actionListAdapter?.notifyDataSetChanged()
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
disposable.add(RxBus
|
||||||
|
.toObservable(EventAutomationUpdateTrigger::class.java)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({
|
||||||
|
event.trigger = it.trigger
|
||||||
|
automation_triggerDescription.text = event.trigger.friendlyDescription()
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
disposable.add(RxBus
|
||||||
|
.toObservable(EventAutomationUpdateAction::class.java)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({
|
||||||
|
event.actions[it.position] = it.action
|
||||||
|
actionListAdapter?.notifyDataSetChanged()
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
disposable.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(bundle: Bundle) {
|
||||||
|
super.onSaveInstanceState(bundle)
|
||||||
|
bundle.putString("event", event.toJSON())
|
||||||
|
bundle.putInt("position", position)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showPreconditions() {
|
||||||
|
val forcedTriggers = event.preconditions
|
||||||
|
if (forcedTriggers.size() > 0) {
|
||||||
|
automation_forcedTriggerDescription.visibility = View.VISIBLE
|
||||||
|
automation_forcedTriggerDescriptionLabel.visibility = View.VISIBLE
|
||||||
|
automation_forcedTriggerDescription.text = forcedTriggers.friendlyDescription()
|
||||||
|
} else {
|
||||||
|
automation_forcedTriggerDescription.visibility = View.GONE
|
||||||
|
automation_forcedTriggerDescriptionLabel.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.dialogs
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateTrigger
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
|
||||||
|
import kotlinx.android.synthetic.main.automation_dialog_edit_trigger.*
|
||||||
|
import kotlinx.android.synthetic.main.okcancel.*
|
||||||
|
|
||||||
|
class EditTriggerDialog : DialogFragment() {
|
||||||
|
|
||||||
|
private var trigger: Trigger? = null
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?): View? {
|
||||||
|
// load data from bundle
|
||||||
|
(savedInstanceState ?: arguments)?.let { bundle ->
|
||||||
|
bundle.getString("trigger")?.let { trigger = Trigger.instantiate(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
return inflater.inflate(R.layout.automation_dialog_edit_trigger, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// display root trigger
|
||||||
|
trigger?.let { it.generateDialog(automation_layoutTrigger, fragmentManager) }
|
||||||
|
|
||||||
|
// OK button
|
||||||
|
ok.setOnClickListener {
|
||||||
|
dismiss()
|
||||||
|
RxBus.send(EventAutomationUpdateTrigger(trigger!!))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel button
|
||||||
|
cancel.setOnClickListener { dismiss() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(bundle: Bundle) {
|
||||||
|
super.onSaveInstanceState(bundle)
|
||||||
|
trigger?.let { bundle.putString("trigger", it.toJSON()) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.dialogs;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector;
|
||||||
|
|
||||||
|
public class TriggerListAdapter {
|
||||||
|
private final LinearLayout mRootLayout;
|
||||||
|
private final FragmentManager mFragmentManager;
|
||||||
|
private final Context mContext;
|
||||||
|
private final TriggerConnector mRootConnector;
|
||||||
|
|
||||||
|
public TriggerListAdapter(FragmentManager fragmentManager, Context context, LinearLayout rootLayout, TriggerConnector rootTrigger) {
|
||||||
|
mRootLayout = rootLayout;
|
||||||
|
mFragmentManager = fragmentManager;
|
||||||
|
mContext = context;
|
||||||
|
mRootConnector = rootTrigger;
|
||||||
|
build(fragmentManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FragmentManager getFM() {
|
||||||
|
return mFragmentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void destroy() {
|
||||||
|
mRootLayout.removeAllViews();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void build(FragmentManager fragmentManager) {
|
||||||
|
for (int i = 0; i < mRootConnector.size(); ++i) {
|
||||||
|
final Trigger trigger = mRootConnector.get(i);
|
||||||
|
|
||||||
|
// spinner
|
||||||
|
if (i > 0) {
|
||||||
|
createSpinner(trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
// trigger layout
|
||||||
|
trigger.generateDialog(mRootLayout, fragmentManager);
|
||||||
|
|
||||||
|
// buttons
|
||||||
|
createButtons(fragmentManager, trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mRootConnector.size() == 0) {
|
||||||
|
Button buttonAdd = new Button(mContext);
|
||||||
|
buttonAdd.setText(MainApp.gs(R.string.addnew));
|
||||||
|
buttonAdd.setOnClickListener(v -> {
|
||||||
|
ChooseTriggerDialog dialog = new ChooseTriggerDialog();
|
||||||
|
dialog.setOnClickListener(newTriggerObject -> {
|
||||||
|
mRootConnector.add(newTriggerObject);
|
||||||
|
rebuild(fragmentManager);
|
||||||
|
});
|
||||||
|
dialog.show(fragmentManager, "ChooseTriggerDialog");
|
||||||
|
});
|
||||||
|
mRootLayout.addView(buttonAdd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Spinner createSpinner() {
|
||||||
|
Spinner spinner = new Spinner(mContext);
|
||||||
|
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(mContext, android.R.layout.simple_spinner_item, TriggerConnector.Type.labels());
|
||||||
|
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinner.setAdapter(spinnerArrayAdapter);
|
||||||
|
return spinner;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createSpinner(Trigger trigger) {
|
||||||
|
final TriggerConnector connector = trigger.getConnector();
|
||||||
|
final int initialPosition = connector.getConnectorType().ordinal();
|
||||||
|
Spinner spinner = createSpinner();
|
||||||
|
spinner.setSelection(initialPosition);
|
||||||
|
spinner.setBackgroundColor(MainApp.gc(R.color.black_overlay));
|
||||||
|
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
params.setMargins(0, MainApp.dpToPx(8), 0, MainApp.dpToPx(8));
|
||||||
|
spinner.setLayoutParams(params);
|
||||||
|
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
if (position != initialPosition) {
|
||||||
|
// connector type changed
|
||||||
|
changeConnector(getFM(), trigger, connector, TriggerConnector.Type.values()[position]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mRootLayout.addView(spinner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createButtons(FragmentManager fragmentManager, Trigger trigger) {
|
||||||
|
// do not create buttons for TriggerConnector
|
||||||
|
if (trigger instanceof TriggerConnector) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button Layout
|
||||||
|
LinearLayout buttonLayout = new LinearLayout(mContext);
|
||||||
|
buttonLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
buttonLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
mRootLayout.addView(buttonLayout);
|
||||||
|
|
||||||
|
// Button [-]
|
||||||
|
Button buttonRemove = new Button(mContext);
|
||||||
|
buttonRemove.setText(MainApp.gs(R.string.delete_short));
|
||||||
|
buttonRemove.setOnClickListener(v -> {
|
||||||
|
final TriggerConnector connector = trigger.getConnector();
|
||||||
|
connector.remove(trigger);
|
||||||
|
connector.simplify().rebuildView(getFM());
|
||||||
|
});
|
||||||
|
buttonLayout.addView(buttonRemove);
|
||||||
|
|
||||||
|
// Button [+]
|
||||||
|
Button buttonAdd = new Button(mContext);
|
||||||
|
buttonAdd.setText(MainApp.gs(R.string.add_short));
|
||||||
|
buttonAdd.setOnClickListener(v -> {
|
||||||
|
ChooseTriggerDialog dialog = new ChooseTriggerDialog();
|
||||||
|
dialog.show(fragmentManager, "ChooseTriggerDialog");
|
||||||
|
dialog.setOnClickListener(newTriggerObject -> {
|
||||||
|
TriggerConnector connector = trigger.getConnector();
|
||||||
|
connector.add(connector.pos(trigger) + 1, newTriggerObject);
|
||||||
|
connector.simplify().rebuildView(getFM());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
buttonLayout.addView(buttonAdd);
|
||||||
|
|
||||||
|
// Button [*]
|
||||||
|
Button buttonCopy = new Button(mContext);
|
||||||
|
buttonCopy.setText(MainApp.gs(R.string.copy_short));
|
||||||
|
buttonCopy.setOnClickListener(v -> {
|
||||||
|
TriggerConnector connector = trigger.getConnector();
|
||||||
|
connector.add(connector.pos(trigger) + 1, trigger.duplicate());
|
||||||
|
connector.simplify().rebuildView(getFM());
|
||||||
|
});
|
||||||
|
buttonLayout.addView(buttonCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void rebuild(FragmentManager fragmentManager) {
|
||||||
|
destroy();
|
||||||
|
build(fragmentManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void changeConnector(final FragmentManager fragmentManager, final Trigger trigger, final TriggerConnector connector, final TriggerConnector.Type newConnectorType) {
|
||||||
|
if (connector.size() > 2) {
|
||||||
|
// split connector
|
||||||
|
int pos = connector.pos(trigger) - 1;
|
||||||
|
|
||||||
|
TriggerConnector newConnector = new TriggerConnector(newConnectorType);
|
||||||
|
|
||||||
|
// move trigger from pos and pos+1 into new connector
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
Trigger t = connector.get(pos);
|
||||||
|
newConnector.add(t);
|
||||||
|
connector.remove(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
connector.add(pos, newConnector);
|
||||||
|
} else {
|
||||||
|
connector.changeConnectorType(newConnectorType);
|
||||||
|
}
|
||||||
|
|
||||||
|
connector.simplify().rebuildView(fragmentManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
|
||||||
|
public class Comparator extends Element {
|
||||||
|
public enum Compare {
|
||||||
|
IS_LESSER,
|
||||||
|
IS_EQUAL_OR_LESSER,
|
||||||
|
IS_EQUAL,
|
||||||
|
IS_EQUAL_OR_GREATER,
|
||||||
|
IS_GREATER,
|
||||||
|
IS_NOT_AVAILABLE;
|
||||||
|
|
||||||
|
public @StringRes
|
||||||
|
int getStringRes() {
|
||||||
|
switch (this) {
|
||||||
|
case IS_LESSER:
|
||||||
|
return R.string.islesser;
|
||||||
|
case IS_EQUAL_OR_LESSER:
|
||||||
|
return R.string.isequalorlesser;
|
||||||
|
case IS_EQUAL:
|
||||||
|
return R.string.isequal;
|
||||||
|
case IS_EQUAL_OR_GREATER:
|
||||||
|
return R.string.isequalorgreater;
|
||||||
|
case IS_GREATER:
|
||||||
|
return R.string.isgreater;
|
||||||
|
case IS_NOT_AVAILABLE:
|
||||||
|
return R.string.isnotavailable;
|
||||||
|
default:
|
||||||
|
return R.string.unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Comparable> boolean check(T obj1, T obj2) {
|
||||||
|
if (obj1 == null || obj2 == null)
|
||||||
|
return this.equals(IS_NOT_AVAILABLE);
|
||||||
|
|
||||||
|
int comparison = obj1.compareTo(obj2);
|
||||||
|
switch (this) {
|
||||||
|
case IS_LESSER:
|
||||||
|
return comparison < 0;
|
||||||
|
case IS_EQUAL_OR_LESSER:
|
||||||
|
return comparison <= 0;
|
||||||
|
case IS_EQUAL:
|
||||||
|
return comparison == 0;
|
||||||
|
case IS_EQUAL_OR_GREATER:
|
||||||
|
return comparison >= 0;
|
||||||
|
case IS_GREATER:
|
||||||
|
return comparison > 0;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> labels() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (Compare c : Compare.values()) {
|
||||||
|
list.add(MainApp.gs(c.getStringRes()));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Compare compare = Compare.IS_EQUAL;
|
||||||
|
|
||||||
|
public Comparator() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator(Comparator another) {
|
||||||
|
super();
|
||||||
|
compare = another.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
Spinner spinner = new Spinner(root.getContext());
|
||||||
|
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, Compare.labels());
|
||||||
|
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinner.setAdapter(spinnerArrayAdapter);
|
||||||
|
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
spinnerParams.setMargins(0, MainApp.dpToPx(4), 0, MainApp.dpToPx(4));
|
||||||
|
spinner.setLayoutParams(spinnerParams);
|
||||||
|
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
compare = Compare.values()[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
spinner.setSelection(compare.ordinal());
|
||||||
|
root.addView(spinner);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Compare getValue() {
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator setValue(Compare compare) {
|
||||||
|
this.compare = compare;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
|
||||||
|
public class ComparatorExists extends Element {
|
||||||
|
public enum Compare {
|
||||||
|
EXISTS,
|
||||||
|
NOT_EXISTS;
|
||||||
|
|
||||||
|
public @StringRes
|
||||||
|
int getStringRes() {
|
||||||
|
switch (this) {
|
||||||
|
case EXISTS:
|
||||||
|
return R.string.exists;
|
||||||
|
case NOT_EXISTS:
|
||||||
|
return R.string.notexists;
|
||||||
|
default:
|
||||||
|
return R.string.unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> labels() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (Compare c : Compare.values()) {
|
||||||
|
list.add(MainApp.gs(c.getStringRes()));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Compare compare = Compare.EXISTS;
|
||||||
|
|
||||||
|
public ComparatorExists() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComparatorExists(ComparatorExists another) {
|
||||||
|
super();
|
||||||
|
compare = another.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
Spinner spinner = new Spinner(root.getContext());
|
||||||
|
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, Compare.labels());
|
||||||
|
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinner.setAdapter(spinnerArrayAdapter);
|
||||||
|
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
spinnerParams.setMargins(0, MainApp.dpToPx(4), 0, MainApp.dpToPx(4));
|
||||||
|
spinner.setLayoutParams(spinnerParams);
|
||||||
|
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
compare = Compare.values()[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
spinner.setSelection(compare.ordinal());
|
||||||
|
root.addView(spinner);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Compare getValue() {
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComparatorExists setValue(Compare compare) {
|
||||||
|
this.compare = compare;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
public abstract class Element {
|
||||||
|
public abstract void addToLayout(LinearLayout root);
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.Constants;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||||
|
import info.nightscout.androidaps.utils.NumberPicker;
|
||||||
|
|
||||||
|
public class InputBg extends Element {
|
||||||
|
|
||||||
|
final TextWatcher textWatcher = new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
if (units.equals(Constants.MMOL)) {
|
||||||
|
value = Math.max(value, 4d);
|
||||||
|
value = Math.min(value, 15d);
|
||||||
|
} else {
|
||||||
|
value = Math.max(value, 72d);
|
||||||
|
value = Math.min(value, 270d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private String units = Constants.MGDL;
|
||||||
|
private double value;
|
||||||
|
double minValue;
|
||||||
|
private double maxValue;
|
||||||
|
private double step;
|
||||||
|
private DecimalFormat decimalFormat;
|
||||||
|
|
||||||
|
public InputBg() {
|
||||||
|
super();
|
||||||
|
setUnits(ProfileFunctions.getInstance().getProfileUnits());
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputBg(InputBg another) {
|
||||||
|
super();
|
||||||
|
value = another.getValue();
|
||||||
|
setUnits(another.getUnits());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
|
||||||
|
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
|
||||||
|
numberPicker.setOnValueChangedListener(value -> this.value = value);
|
||||||
|
root.addView(numberPicker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnits() {
|
||||||
|
return units;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputBg setUnits(String units) {
|
||||||
|
// set default initial value
|
||||||
|
if (units.equals(Constants.MMOL)) {
|
||||||
|
// mmol
|
||||||
|
minValue = 2;
|
||||||
|
maxValue = 30;
|
||||||
|
step = 0.1;
|
||||||
|
decimalFormat = new DecimalFormat("0.0");
|
||||||
|
} else {
|
||||||
|
// mg/dL
|
||||||
|
minValue = 40;
|
||||||
|
maxValue = 540;
|
||||||
|
step = 1;
|
||||||
|
decimalFormat = new DecimalFormat("0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that value is in range
|
||||||
|
textWatcher.afterTextChanged(null);
|
||||||
|
|
||||||
|
this.units = units;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputBg setValue(double value) {
|
||||||
|
this.value = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
public class InputButton extends Element {
|
||||||
|
|
||||||
|
String text;
|
||||||
|
Runnable runnable;
|
||||||
|
|
||||||
|
public InputButton(String text, Runnable runnable) {
|
||||||
|
this.text = text;
|
||||||
|
this.runnable = runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
Button button = new Button(root.getContext());
|
||||||
|
|
||||||
|
button.setText(text);
|
||||||
|
button.setOnClickListener(view -> {
|
||||||
|
if (runnable != null)
|
||||||
|
runnable.run();
|
||||||
|
});
|
||||||
|
|
||||||
|
root.addView(button);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.utils.NumberPicker;
|
||||||
|
|
||||||
|
public class InputDelta extends Element {
|
||||||
|
private Comparator.Compare compare = Comparator.Compare.IS_EQUAL;
|
||||||
|
final TextWatcher textWatcher = new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public enum DeltaType {
|
||||||
|
DELTA,
|
||||||
|
SHORT_AVERAGE,
|
||||||
|
LONG_AVERAGE;
|
||||||
|
|
||||||
|
public @StringRes
|
||||||
|
int getStringRes() {
|
||||||
|
switch (this) {
|
||||||
|
case DELTA:
|
||||||
|
return R.string.delta;
|
||||||
|
case SHORT_AVERAGE:
|
||||||
|
return R.string.short_avgdelta;
|
||||||
|
case LONG_AVERAGE:
|
||||||
|
return R.string.long_avgdelta;
|
||||||
|
default:
|
||||||
|
return R.string.unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> labels() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (DeltaType d : DeltaType.values()) {
|
||||||
|
list.add(MainApp.gs(d.getStringRes()));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double value;
|
||||||
|
double minValue;
|
||||||
|
double maxValue;
|
||||||
|
private double step;
|
||||||
|
private DecimalFormat decimalFormat;
|
||||||
|
private DeltaType deltaType;
|
||||||
|
|
||||||
|
NumberPicker numberPicker;
|
||||||
|
|
||||||
|
public InputDelta() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputDelta(double value, double minValue, double maxValue, double step, DecimalFormat decimalFormat, DeltaType deltaType) {
|
||||||
|
super();
|
||||||
|
this.value = value;
|
||||||
|
this.minValue = minValue;
|
||||||
|
this.maxValue = maxValue;
|
||||||
|
this.step = step;
|
||||||
|
this.decimalFormat = decimalFormat;
|
||||||
|
this.deltaType = deltaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputDelta(InputDelta another) {
|
||||||
|
super();
|
||||||
|
value = another.getValue();
|
||||||
|
minValue = another.minValue;
|
||||||
|
maxValue = another.maxValue;
|
||||||
|
step = another.step;
|
||||||
|
decimalFormat = another.decimalFormat;
|
||||||
|
deltaType = another.deltaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
Spinner spinner = new Spinner(root.getContext());
|
||||||
|
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(root.getContext(), android.R.layout.simple_spinner_item, DeltaType.labels());
|
||||||
|
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinner.setAdapter(spinnerArrayAdapter);
|
||||||
|
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||||
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||||
|
);
|
||||||
|
spinnerParams.setMargins(0, MainApp.dpToPx(4), 0, MainApp.dpToPx(4));
|
||||||
|
spinner.setLayoutParams(spinnerParams);
|
||||||
|
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
deltaType = DeltaType.values()[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
spinner.setSelection(this.deltaType.ordinal());
|
||||||
|
// root.addView(spinner);
|
||||||
|
numberPicker = new NumberPicker(root.getContext(), null);
|
||||||
|
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
|
||||||
|
numberPicker.setOnValueChangedListener(value -> this.value = value);
|
||||||
|
LinearLayout l = new LinearLayout(root.getContext());
|
||||||
|
l.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
l.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
l.addView(spinner);
|
||||||
|
l.addView(numberPicker);
|
||||||
|
root.addView(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputDelta setValue(double value, DeltaType type) {
|
||||||
|
this.value = value;
|
||||||
|
this.deltaType = type;
|
||||||
|
if (numberPicker != null)
|
||||||
|
numberPicker.setValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeltaType getDeltaType() {
|
||||||
|
return deltaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.utils.NumberPicker;
|
||||||
|
|
||||||
|
public class InputDouble extends Element {
|
||||||
|
|
||||||
|
final TextWatcher textWatcher = new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private double value;
|
||||||
|
double minValue;
|
||||||
|
double maxValue;
|
||||||
|
private double step;
|
||||||
|
private DecimalFormat decimalFormat;
|
||||||
|
|
||||||
|
NumberPicker numberPicker;
|
||||||
|
|
||||||
|
public InputDouble() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputDouble(double value, double minValue, double maxValue, double step, DecimalFormat decimalFormat) {
|
||||||
|
super();
|
||||||
|
this.value = value;
|
||||||
|
this.minValue = minValue;
|
||||||
|
this.maxValue = maxValue;
|
||||||
|
this.step = step;
|
||||||
|
this.decimalFormat = decimalFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputDouble(InputDouble another) {
|
||||||
|
super();
|
||||||
|
value = another.getValue();
|
||||||
|
minValue = another.minValue;
|
||||||
|
maxValue = another.maxValue;
|
||||||
|
step = another.step;
|
||||||
|
decimalFormat = another.decimalFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
numberPicker = new NumberPicker(root.getContext(), null);
|
||||||
|
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, textWatcher);
|
||||||
|
numberPicker.setOnValueChangedListener(value -> this.value = value);
|
||||||
|
root.addView(numberPicker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputDouble setValue(double value) {
|
||||||
|
this.value = value;
|
||||||
|
if (numberPicker != null)
|
||||||
|
numberPicker.setValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.utils.NumberPicker;
|
||||||
|
|
||||||
|
public class InputDuration extends Element {
|
||||||
|
public enum TimeUnit {
|
||||||
|
MINUTES,
|
||||||
|
HOURS
|
||||||
|
}
|
||||||
|
|
||||||
|
private TimeUnit unit;
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
public InputDuration(int value, TimeUnit unit) {
|
||||||
|
this.unit = unit;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
|
||||||
|
if (unit.equals(TimeUnit.MINUTES)) {
|
||||||
|
// Minutes
|
||||||
|
numberPicker.setParams(0d, 0d, 24 * 60d, 10d, new DecimalFormat("0"), false, null);
|
||||||
|
} else {
|
||||||
|
// Hours
|
||||||
|
numberPicker.setParams(0d, 0d, 24d, 1d, new DecimalFormat("0"), false, null);
|
||||||
|
}
|
||||||
|
numberPicker.setValue((double) value);
|
||||||
|
numberPicker.setOnValueChangedListener(value -> this.value = (int) value);
|
||||||
|
root.addView(numberPicker);
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeUnit getUnit() {
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinutes(int value) {
|
||||||
|
if (unit.equals(TimeUnit.MINUTES)) {
|
||||||
|
this.value = value;
|
||||||
|
} else {
|
||||||
|
this.value = value / 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinutes() {
|
||||||
|
if (unit.equals(TimeUnit.MINUTES)) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return value * 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.utils.NumberPicker;
|
||||||
|
|
||||||
|
public class InputInsulin extends Element {
|
||||||
|
|
||||||
|
final TextWatcher textWatcher = new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
value = Math.max(value, -20d);
|
||||||
|
value = Math.min(value, 20d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private double value;
|
||||||
|
|
||||||
|
public InputInsulin() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputInsulin(InputInsulin another) {
|
||||||
|
super();
|
||||||
|
value = another.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
|
||||||
|
numberPicker.setParams(0d, -20d, 20d, 0.1, new DecimalFormat("0.0"), true, null, textWatcher);
|
||||||
|
numberPicker.setValue(value);
|
||||||
|
numberPicker.setOnValueChangedListener(value -> this.value = value);
|
||||||
|
root.addView(numberPicker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputInsulin setValue(double value) {
|
||||||
|
this.value = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.utils.NumberPicker;
|
||||||
|
|
||||||
|
public class InputPercent extends Element {
|
||||||
|
|
||||||
|
final TextWatcher textWatcher = new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
value = Math.max(value, 70d);
|
||||||
|
value = Math.min(value, 130d);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private double value;
|
||||||
|
|
||||||
|
public InputPercent() {
|
||||||
|
super();
|
||||||
|
value = 100d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputPercent(InputPercent another) {
|
||||||
|
super();
|
||||||
|
value = another.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
NumberPicker numberPicker = new NumberPicker(root.getContext(), null);
|
||||||
|
numberPicker.setParams(100d, 70d, 130d, 5d, new DecimalFormat("0"), true, null, textWatcher);
|
||||||
|
numberPicker.setValue(value);
|
||||||
|
numberPicker.setOnValueChangedListener(value -> this.value = value);
|
||||||
|
root.addView(numberPicker);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputPercent setValue(double value) {
|
||||||
|
this.value = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
public class InputString extends Element {
|
||||||
|
|
||||||
|
TextWatcher textWatcher = new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
value = s.toString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private String value = "";
|
||||||
|
|
||||||
|
public InputString() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputString(InputString another) {
|
||||||
|
super();
|
||||||
|
value = another.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
EditText editText = new EditText(root.getContext());
|
||||||
|
editText.setText(value);
|
||||||
|
editText.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
editText.addTextChangedListener(textWatcher);
|
||||||
|
root.addView(editText);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputString setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
|
||||||
|
public class LabelWithElement extends Element {
|
||||||
|
final Element element;
|
||||||
|
final String textPre;
|
||||||
|
final String textPost;
|
||||||
|
|
||||||
|
public LabelWithElement(String textPre, String textPost, Element element) {
|
||||||
|
this.element = element;
|
||||||
|
this.textPre = textPre;
|
||||||
|
this.textPost = textPost;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
// container layout
|
||||||
|
LinearLayout layout = new LinearLayout(root.getContext());
|
||||||
|
layout.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
layout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
// text view pre element
|
||||||
|
int px = MainApp.dpToPx(10);
|
||||||
|
TextView textViewPre = new TextView(root.getContext());
|
||||||
|
textViewPre.setText(textPre);
|
||||||
|
textViewPre.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
//textViewPre.setWidth(MainApp.dpToPx(120));
|
||||||
|
textViewPre.setPadding(px, px, px, px);
|
||||||
|
textViewPre.setTypeface(textViewPre.getTypeface(), Typeface.BOLD);
|
||||||
|
layout.addView(textViewPre);
|
||||||
|
|
||||||
|
// add element to layout
|
||||||
|
element.addToLayout(layout);
|
||||||
|
|
||||||
|
// text view post element
|
||||||
|
if (textPost != null) {
|
||||||
|
px = MainApp.dpToPx(5);
|
||||||
|
TextView textViewPost = new TextView(root.getContext());
|
||||||
|
textViewPost.setText(textPost);
|
||||||
|
textViewPost.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
//textViewPost.setWidth(MainApp.dpToPx(45));
|
||||||
|
textViewPost.setPadding(px, px, px, px);
|
||||||
|
textViewPost.setTypeface(textViewPost.getTypeface(), Typeface.BOLD);
|
||||||
|
layout.addView(textViewPost);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add layout to root layout
|
||||||
|
root.addView(layout);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class LayoutBuilder {
|
||||||
|
ArrayList<Element> mElements = new ArrayList<>();
|
||||||
|
|
||||||
|
public LayoutBuilder add(Element element) {
|
||||||
|
mElements.add(element);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LayoutBuilder add(Element element, boolean condition) {
|
||||||
|
if (condition) mElements.add(element);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void build(LinearLayout layout) {
|
||||||
|
for (Element e : mElements) {
|
||||||
|
e.addToLayout(layout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.elements;
|
||||||
|
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
|
||||||
|
public class StaticLabel extends Element {
|
||||||
|
String label;
|
||||||
|
|
||||||
|
public StaticLabel(String label) {
|
||||||
|
super();
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StaticLabel(int resourceId) {
|
||||||
|
super();
|
||||||
|
this.label = MainApp.gs(resourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addToLayout(LinearLayout root) {
|
||||||
|
// text view pre element
|
||||||
|
int px = MainApp.dpToPx(10);
|
||||||
|
TextView textView = new TextView(root.getContext());
|
||||||
|
textView.setText(label);
|
||||||
|
// textViewPre.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
textView.setPadding(px, px, px, px);
|
||||||
|
textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
|
||||||
|
textView.setBackgroundColor(MainApp.gc(R.color.mdtp_line_dark));
|
||||||
|
// add element to layout
|
||||||
|
root.addView(textView);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.actions.Action
|
||||||
|
|
||||||
|
class EventAutomationAddAction(val action: Action) : Event()
|
|
@ -0,0 +1,5 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
|
||||||
|
class EventAutomationDataChanged : Event()
|
|
@ -0,0 +1,7 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.actions.Action
|
||||||
|
|
||||||
|
class EventAutomationUpdateAction(val action: Action, val position : Int) : Event() {
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
|
||||||
|
class EventAutomationUpdateGui : Event()
|
|
@ -0,0 +1,6 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.events
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.events.Event
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
|
||||||
|
|
||||||
|
class EventAutomationUpdateTrigger(val trigger: Trigger) : Event()
|
|
@ -0,0 +1,94 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public abstract class Trigger {
|
||||||
|
|
||||||
|
TriggerConnector connector = null;
|
||||||
|
long lastRun;
|
||||||
|
|
||||||
|
Trigger() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerConnector getConnector() {
|
||||||
|
return connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean shouldRun();
|
||||||
|
|
||||||
|
|
||||||
|
public abstract String toJSON();
|
||||||
|
|
||||||
|
/*package*/
|
||||||
|
abstract Trigger fromJSON(String data);
|
||||||
|
|
||||||
|
public abstract int friendlyName();
|
||||||
|
|
||||||
|
public abstract String friendlyDescription();
|
||||||
|
|
||||||
|
public abstract Optional<Integer> icon();
|
||||||
|
|
||||||
|
public void executed(long time) {
|
||||||
|
lastRun = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastRun() {
|
||||||
|
return lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Trigger duplicate();
|
||||||
|
|
||||||
|
public static Trigger instantiate(String json) {
|
||||||
|
try {
|
||||||
|
return instantiate(new JSONObject(json));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Trigger instantiate(JSONObject object) {
|
||||||
|
try {
|
||||||
|
String type = object.getString("type");
|
||||||
|
JSONObject data = object.getJSONObject("data");
|
||||||
|
Class clazz = Class.forName(type);
|
||||||
|
return ((Trigger) clazz.newInstance()).fromJSON(data.toString());
|
||||||
|
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
TextView title = new TextView(root.getContext());
|
||||||
|
title.setText(friendlyName());
|
||||||
|
root.addView(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Activity scanForActivity(Context cont) {
|
||||||
|
if (cont == null)
|
||||||
|
return null;
|
||||||
|
else if (cont instanceof Activity)
|
||||||
|
return (Activity) cont;
|
||||||
|
else if (cont instanceof ContextWrapper)
|
||||||
|
return scanForActivity(((ContextWrapper) cont).getBaseContext());
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData;
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.SP;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerAutosensValue extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
private final int minValue = (int) (SP.getDouble("openapsama_autosens_min", 0.7d) * 100);
|
||||||
|
private final int maxValue = (int) (SP.getDouble("openapsama_autosens_max", 1.2d) * 100);
|
||||||
|
private final double step = 1;
|
||||||
|
private DecimalFormat decimalFormat = new DecimalFormat("1");
|
||||||
|
private InputDouble value = new InputDouble(100, (double) minValue, (double) maxValue, step, decimalFormat);
|
||||||
|
private Comparator comparator = new Comparator();
|
||||||
|
|
||||||
|
public TriggerAutosensValue() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerAutosensValue(TriggerAutosensValue triggerAutosensValue) {
|
||||||
|
super();
|
||||||
|
value = new InputDouble(triggerAutosensValue.value);
|
||||||
|
lastRun = triggerAutosensValue.lastRun;
|
||||||
|
comparator = new Comparator(triggerAutosensValue.comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
AutosensData autosensData = IobCobCalculatorPlugin.getPlugin().getLastAutosensData("Automation trigger");
|
||||||
|
if (autosensData == null)
|
||||||
|
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean doRun = comparator.getValue().check((autosensData.autosensResult.ratio), getValue() / 100d);
|
||||||
|
if (doRun) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerAutosensValue.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("value", getValue());
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
value.setValue(JsonHelper.safeGetDouble(d, "value"));
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.autosenslabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.autosenscompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.as);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerAutosensValue(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerAutosensValue setValue(int requestedValue) {
|
||||||
|
this.value.setValue(requestedValue);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerAutosensValue lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerAutosensValue comparator(Comparator.Compare compare) {
|
||||||
|
this.comparator = new Comparator().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.autosenslabel))
|
||||||
|
.add(comparator)
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.autosenslabel) + ": ", "", value))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.Constants;
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputBg;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerBg extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
private InputBg bg = new InputBg();
|
||||||
|
private Comparator comparator = new Comparator();
|
||||||
|
|
||||||
|
public TriggerBg() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerBg(TriggerBg triggerBg) {
|
||||||
|
super();
|
||||||
|
bg = new InputBg(triggerBg.bg);
|
||||||
|
comparator = new Comparator(triggerBg.comparator);
|
||||||
|
lastRun = triggerBg.lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return bg.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnits() {
|
||||||
|
return bg.getUnits();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastRun() {
|
||||||
|
return lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (glucoseStatus == null && comparator.getValue().equals(Comparator.Compare.IS_NOT_AVAILABLE)) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (glucoseStatus == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean doRun = comparator.getValue().check(glucoseStatus.glucose, Profile.toMgdl(bg.getValue(), bg.getUnits()));
|
||||||
|
if (doRun) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerBg.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("bg", bg.getValue());
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
data.put("units", bg.getUnits());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
bg.setUnits(JsonHelper.safeGetString(d, "units"));
|
||||||
|
bg.setValue(JsonHelper.safeGetDouble(d, "bg"));
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.glucose;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
if (comparator.getValue().equals(Comparator.Compare.IS_NOT_AVAILABLE))
|
||||||
|
return MainApp.gs(R.string.glucoseisnotavailable);
|
||||||
|
else {
|
||||||
|
return MainApp.gs(bg.getUnits().equals(Constants.MGDL) ? R.string.glucosecomparedmgdl : R.string.glucosecomparedmmol, MainApp.gs(comparator.getValue().getStringRes()), bg.getValue(), bg.getUnits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.icon_cp_bgcheck);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerBg(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerBg setValue(double value) {
|
||||||
|
bg.setValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerBg lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerBg comparator(Comparator.Compare compare) {
|
||||||
|
this.comparator = new Comparator().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerBg setUnits(String units) {
|
||||||
|
bg.setUnits(units);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.glucose))
|
||||||
|
.add(comparator)
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.glucose_u, bg.getUnits()), "", bg))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerBolusAgo extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
private final double step = 1;
|
||||||
|
private DecimalFormat decimalFormat = new DecimalFormat("1");
|
||||||
|
public InputDuration minutesAgo = new InputDuration(0, InputDuration.TimeUnit.MINUTES);
|
||||||
|
private Comparator comparator = new Comparator();
|
||||||
|
|
||||||
|
public TriggerBolusAgo() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerBolusAgo(TriggerBolusAgo triggerBolusAgo) {
|
||||||
|
super();
|
||||||
|
minutesAgo = new InputDuration(triggerBolusAgo.minutesAgo.getMinutes(), InputDuration.TimeUnit.MINUTES);
|
||||||
|
lastRun = triggerBolusAgo.lastRun;
|
||||||
|
comparator = new Comparator(triggerBolusAgo.comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return minutesAgo.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
long lastBolusTime = TreatmentsPlugin.getPlugin().getLastBolusTime(false);
|
||||||
|
|
||||||
|
if (lastBolusTime == 0)
|
||||||
|
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double minutesAgo = (double) (DateUtil.now() - lastBolusTime) / (60 * 1000);
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("LastBolus min ago: " + minutesAgo);
|
||||||
|
|
||||||
|
boolean doRun = comparator.getValue().check((minutesAgo), getValue());
|
||||||
|
if (doRun) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerBolusAgo.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("minutesAgo", getValue());
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
minutesAgo.setMinutes(JsonHelper.safeGetInt(d, "minutesAgo"));
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.lastboluslabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.lastboluscompared, MainApp.gs(comparator.getValue().getStringRes()), (int) getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.icon_bolus);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerBolusAgo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerBolusAgo setValue(int requestedValue) {
|
||||||
|
this.minutesAgo.setMinutes(requestedValue);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerBolusAgo lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerBolusAgo comparator(Comparator.Compare compare) {
|
||||||
|
this.comparator = new Comparator().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.lastboluslabel))
|
||||||
|
.add(comparator)
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.lastboluslabel) + ": ", "", minutesAgo))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo;
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.SP;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerCOB extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
private final int minValue = 0;
|
||||||
|
private final int maxValue = (int) (SP.getInt(R.string.key_treatmentssafety_maxcarbs, 48));
|
||||||
|
private final double step = 1;
|
||||||
|
private DecimalFormat decimalFormat = new DecimalFormat("1");
|
||||||
|
private InputDouble value = new InputDouble(0, (double) minValue, (double) maxValue, step, decimalFormat);
|
||||||
|
private Comparator comparator = new Comparator();
|
||||||
|
|
||||||
|
public TriggerCOB() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerCOB(TriggerCOB triggerCOB) {
|
||||||
|
super();
|
||||||
|
value = new InputDouble(triggerCOB.value);
|
||||||
|
lastRun = triggerCOB.lastRun;
|
||||||
|
comparator = new Comparator(triggerCOB.comparator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return value.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
CobInfo cobInfo = IobCobCalculatorPlugin.getPlugin().getCobInfo(false, "AutomationTriggerCOB");
|
||||||
|
if (cobInfo == null)
|
||||||
|
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean doRun = comparator.getValue().check((cobInfo.displayCob), getValue());
|
||||||
|
if (doRun) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerCOB.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("carbs", getValue());
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
value.setValue(JsonHelper.safeGetDouble(d, "carbs"));
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.triggercoblabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.cobcompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.icon_cp_bolus_carbs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerCOB(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerCOB setValue(int requestedValue) {
|
||||||
|
this.value.setValue(requestedValue);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerCOB lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerCOB comparator(Comparator.Compare compare) {
|
||||||
|
this.comparator = new Comparator().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.triggercoblabel))
|
||||||
|
.add(comparator)
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.triggercoblabel) + ": ", "", value))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,277 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.dialogs.TriggerListAdapter;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
|
||||||
|
public class TriggerConnector extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
AND,
|
||||||
|
OR,
|
||||||
|
XOR;
|
||||||
|
|
||||||
|
public boolean apply(boolean a, boolean b) {
|
||||||
|
switch (this) {
|
||||||
|
case AND:
|
||||||
|
return a && b;
|
||||||
|
case OR:
|
||||||
|
return a || b;
|
||||||
|
case XOR:
|
||||||
|
return a ^ b;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @StringRes
|
||||||
|
int getStringRes() {
|
||||||
|
switch (this) {
|
||||||
|
case OR:
|
||||||
|
return R.string.or;
|
||||||
|
case XOR:
|
||||||
|
return R.string.xor;
|
||||||
|
|
||||||
|
default:
|
||||||
|
case AND:
|
||||||
|
return R.string.and;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> labels() {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
for (Type t : values()) {
|
||||||
|
list.add(MainApp.gs(t.getStringRes()));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fillIconSet(TriggerConnector connector, HashSet<Integer> set) {
|
||||||
|
for (Trigger t : connector.list) {
|
||||||
|
if (t instanceof TriggerConnector) {
|
||||||
|
fillIconSet((TriggerConnector) t, set);
|
||||||
|
} else {
|
||||||
|
Optional<Integer> icon = t.icon();
|
||||||
|
if (icon.isPresent()) {
|
||||||
|
set.add(icon.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Trigger> list = new ArrayList<>();
|
||||||
|
private Type connectorType;
|
||||||
|
|
||||||
|
public TriggerConnector() {
|
||||||
|
connectorType = Type.AND;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerConnector(Type connectorType) {
|
||||||
|
this.connectorType = connectorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void changeConnectorType(Type type) {
|
||||||
|
this.connectorType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getConnectorType() {
|
||||||
|
return connectorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void add(Trigger t) {
|
||||||
|
list.add(t);
|
||||||
|
t.connector = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void add(int pos, Trigger t) {
|
||||||
|
list.add(pos, t);
|
||||||
|
t.connector = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean remove(Trigger t) {
|
||||||
|
return list.remove(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Trigger get(int i) {
|
||||||
|
return list.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int pos(Trigger trigger) {
|
||||||
|
for (int i = 0; i < list.size(); ++i) {
|
||||||
|
if (list.get(i) == trigger) return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
boolean result = true;
|
||||||
|
|
||||||
|
// check first trigger
|
||||||
|
if (list.size() > 0)
|
||||||
|
result = list.get(0).shouldRun();
|
||||||
|
|
||||||
|
// check all others
|
||||||
|
for (int i = 1; i < list.size(); ++i) {
|
||||||
|
result = connectorType.apply(result, list.get(i).shouldRun());
|
||||||
|
}
|
||||||
|
if (result)
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription().replace("\n", " "));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerConnector.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("connectorType", connectorType.toString());
|
||||||
|
JSONArray array = new JSONArray();
|
||||||
|
for (Trigger t : list) {
|
||||||
|
array.put(t.toJSON());
|
||||||
|
}
|
||||||
|
data.put("triggerList", array);
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
connectorType = Type.valueOf(JsonHelper.safeGetString(d, "connectorType"));
|
||||||
|
JSONArray array = d.getJSONArray("triggerList");
|
||||||
|
list.clear();
|
||||||
|
for (int i = 0; i < array.length(); i++) {
|
||||||
|
Trigger newItem = instantiate(new JSONObject(array.getString(i)));
|
||||||
|
add(newItem);
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return connectorType.getStringRes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
int counter = 0;
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
for (Trigger t : list) {
|
||||||
|
if (counter++ > 0) result.append("\n").append(MainApp.gs(friendlyName())).append("\n");
|
||||||
|
result.append(t.friendlyDescription());
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executed(long time) {
|
||||||
|
for (int i = 0; i < list.size(); ++i) {
|
||||||
|
list.get(i).executed(time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerListAdapter adapter;
|
||||||
|
|
||||||
|
public void rebuildView(FragmentManager fragmentManager) {
|
||||||
|
if (adapter != null)
|
||||||
|
adapter.rebuild(fragmentManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
final int padding = MainApp.dpToPx(5);
|
||||||
|
|
||||||
|
root.setPadding(padding, padding, padding, padding);
|
||||||
|
root.setBackgroundResource(R.drawable.border_automation_unit);
|
||||||
|
|
||||||
|
LinearLayout triggerListLayout = new LinearLayout(root.getContext());
|
||||||
|
triggerListLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
triggerListLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
root.addView(triggerListLayout);
|
||||||
|
|
||||||
|
adapter = new TriggerListAdapter(fragmentManager, root.getContext(), triggerListLayout, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerConnector simplify() {
|
||||||
|
// simplify children
|
||||||
|
for (int i = 0; i < size(); ++i) {
|
||||||
|
if (get(i) instanceof TriggerConnector) {
|
||||||
|
TriggerConnector t = (TriggerConnector) get(i);
|
||||||
|
t.simplify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop connector with only 1 element
|
||||||
|
if (size() == 1 && get(0) instanceof TriggerConnector) {
|
||||||
|
TriggerConnector c = (TriggerConnector) get(0);
|
||||||
|
remove(c);
|
||||||
|
changeConnectorType(c.getConnectorType());
|
||||||
|
for (Trigger t : c.list) {
|
||||||
|
add(t);
|
||||||
|
}
|
||||||
|
c.list.clear();
|
||||||
|
return simplify();
|
||||||
|
}
|
||||||
|
|
||||||
|
// merge connectors
|
||||||
|
if (connector != null && (connector.getConnectorType().equals(connectorType) || size() == 1)) {
|
||||||
|
final int pos = connector.pos(this);
|
||||||
|
connector.remove(this);
|
||||||
|
// move triggers of child connector into parent connector
|
||||||
|
for (int i = size() - 1; i >= 0; --i) {
|
||||||
|
connector.add(pos, get(i));
|
||||||
|
}
|
||||||
|
list.clear();
|
||||||
|
return connector.simplify();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,208 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.Constants;
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDelta;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDelta.DeltaType;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerDelta extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
private double minValue = 0d;
|
||||||
|
private double maxValue = 1d;
|
||||||
|
private double step = 1;
|
||||||
|
private DecimalFormat decimalFormat = new DecimalFormat("1");
|
||||||
|
private String units;
|
||||||
|
private DeltaType deltaType;
|
||||||
|
|
||||||
|
private InputDelta value = new InputDelta( (double) minValue,(double) minValue, (double) maxValue, step, decimalFormat, deltaType);
|
||||||
|
private Comparator comparator = new Comparator();
|
||||||
|
|
||||||
|
public TriggerDelta() {
|
||||||
|
super();
|
||||||
|
this.units = ProfileFunctions.getInstance().getProfileUnits();
|
||||||
|
initializer();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerDelta(TriggerDelta triggerDelta) {
|
||||||
|
super();
|
||||||
|
this.units = ProfileFunctions.getInstance().getProfileUnits();
|
||||||
|
initializer();
|
||||||
|
value = triggerDelta.value;
|
||||||
|
lastRun = triggerDelta.lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
deltaType = value.getDeltaType();
|
||||||
|
return value.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DeltaType getType() {
|
||||||
|
return deltaType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnits() {
|
||||||
|
return this.units;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData();
|
||||||
|
if (glucoseStatus == null)
|
||||||
|
if (comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Setting type of delta
|
||||||
|
double delta;
|
||||||
|
|
||||||
|
if (deltaType == DeltaType.SHORT_AVERAGE)
|
||||||
|
delta = glucoseStatus.short_avgdelta;
|
||||||
|
else if (deltaType == DeltaType.LONG_AVERAGE)
|
||||||
|
delta = glucoseStatus.long_avgdelta;
|
||||||
|
else
|
||||||
|
delta = glucoseStatus.delta;
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean doRun = comparator.getValue().check(delta, Profile.toMgdl(value.getValue(), this.units));
|
||||||
|
if (doRun) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: delta is " + delta + " " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerDelta.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("value", getValue());
|
||||||
|
data.put("units", units);
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("deltaType", getType());
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
units = JsonHelper.safeGetString(d, "units");
|
||||||
|
deltaType = DeltaType.valueOf(JsonHelper.safeGetString(d, "deltaType", ""));
|
||||||
|
value.setValue(JsonHelper.safeGetDouble(d, "value"), deltaType);
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.deltalabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.deltacompared, MainApp.gs(comparator.getValue().getStringRes()), getValue(), MainApp.gs(deltaType.getStringRes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.icon_auto_delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerDelta(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerDelta setValue(double requestedValue, DeltaType requestedType) {
|
||||||
|
this.value.setValue(requestedValue, requestedType);
|
||||||
|
this.deltaType = requestedType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerDelta setUnits(String units) {
|
||||||
|
this.units = units;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializer(){
|
||||||
|
if (this.units.equals(Constants.MMOL)) {
|
||||||
|
this.maxValue = 4d;
|
||||||
|
this.minValue = -4d;
|
||||||
|
this.step = 0.1d;
|
||||||
|
this.decimalFormat = new DecimalFormat("0.1");
|
||||||
|
this.deltaType = DeltaType.DELTA;
|
||||||
|
} else {
|
||||||
|
this.maxValue = 72d;
|
||||||
|
this.minValue = -72d;
|
||||||
|
this.step = 1d;
|
||||||
|
this.deltaType = DeltaType.DELTA;
|
||||||
|
}
|
||||||
|
value = new InputDelta( (double) minValue,(double) minValue, (double) maxValue, step, decimalFormat, deltaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TriggerDelta lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerDelta comparator(Comparator.Compare compare) {
|
||||||
|
this.comparator = new Comparator().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.deltalabel))
|
||||||
|
.add(comparator)
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.deltalabel) + ": ", "", value))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.IobTotal;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputInsulin;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerIob extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
private InputInsulin insulin = new InputInsulin();
|
||||||
|
private Comparator comparator = new Comparator();
|
||||||
|
|
||||||
|
public TriggerIob() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerIob(TriggerIob triggerIob) {
|
||||||
|
super();
|
||||||
|
insulin = new InputInsulin(triggerIob.insulin);
|
||||||
|
comparator = new Comparator(triggerIob.comparator);
|
||||||
|
lastRun = triggerIob.lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return insulin.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
Profile profile = ProfileFunctions.getInstance().getProfile();
|
||||||
|
if (profile == null)
|
||||||
|
return false;
|
||||||
|
IobTotal iob = IobCobCalculatorPlugin.getPlugin().calculateFromTreatmentsAndTempsSynchronized(DateUtil.now(), profile);
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean doRun = comparator.getValue().check(iob.iob, getValue());
|
||||||
|
if (doRun) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerIob.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("insulin", getValue());
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
insulin.setValue(JsonHelper.safeGetDouble(d, "insulin"));
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.iob;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.iobcompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_keyboard_capslock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerIob(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerIob setValue(double threshold) {
|
||||||
|
insulin.setValue(threshold);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerIob lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerIob comparator(Comparator.Compare compare) {
|
||||||
|
this.comparator = new Comparator().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.iob))
|
||||||
|
.add(comparator)
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.iob_u), "", insulin))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.location.Location;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputButton;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputDouble;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputString;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.services.LocationService;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerLocation extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
InputDouble latitude = new InputDouble(0d, -90d, +90d, 0.000001d, new DecimalFormat("0.000000"));
|
||||||
|
InputDouble longitude = new InputDouble(0d, -180d, +180d, 0.000001d, new DecimalFormat("0.000000"));
|
||||||
|
InputDouble distance = new InputDouble(200d, 0, 100000, 10d, new DecimalFormat("0"));
|
||||||
|
InputString name = new InputString();
|
||||||
|
|
||||||
|
private Runnable buttonAction = () -> {
|
||||||
|
Location location = LocationService.getLastLocation();
|
||||||
|
if (location != null) {
|
||||||
|
latitude.setValue(location.getLatitude());
|
||||||
|
longitude.setValue(location.getLongitude());
|
||||||
|
log.debug(String.format("Grabbed location: %f %f", latitude.getValue(), longitude.getValue()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public TriggerLocation() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerLocation(TriggerLocation triggerLocation) {
|
||||||
|
super();
|
||||||
|
latitude = new InputDouble(triggerLocation.latitude.getValue(), -90d, +90d, 0.00001d, new DecimalFormat("0.00000"));
|
||||||
|
longitude = new InputDouble(triggerLocation.longitude.getValue(), -180d, +180d, 0.00001d, new DecimalFormat("0.00000"));
|
||||||
|
distance = new InputDouble(200d, 0, 100000, 10d, new DecimalFormat("0"));
|
||||||
|
lastRun = triggerLocation.lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
Location location = LocationService.getLastLocation();
|
||||||
|
if (location == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Location a = new Location("Trigger");
|
||||||
|
a.setLatitude(latitude.getValue());
|
||||||
|
a.setLongitude(longitude.getValue());
|
||||||
|
double calculatedDistance = location.distanceTo(a);
|
||||||
|
|
||||||
|
if (calculatedDistance < distance.getValue()) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerLocation.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("latitude", latitude.getValue());
|
||||||
|
data.put("longitude", longitude.getValue());
|
||||||
|
data.put("distance", distance.getValue());
|
||||||
|
data.put("name", name.getValue());
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
latitude.setValue(JsonHelper.safeGetDouble(d, "latitude"));
|
||||||
|
longitude.setValue(JsonHelper.safeGetDouble(d, "longitude"));
|
||||||
|
distance.setValue(JsonHelper.safeGetDouble(d, "distance"));
|
||||||
|
name.setValue(JsonHelper.safeGetString(d, "name"));
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.locationis, name.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_location_on);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerLocation(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TriggerLocation setLatitude(double value) {
|
||||||
|
latitude.setValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerLocation setLongitude(double value) {
|
||||||
|
longitude.setValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerLocation setdistance(double value) {
|
||||||
|
distance.setValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerLocation lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.location))
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.name_short), "", name))
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.latitude_short), "", latitude))
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.longitude_short), "", longitude))
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.distance_short), "", distance))
|
||||||
|
.add(new InputButton(MainApp.gs(R.string.currentlocation), buttonAction), LocationService.getLastLocation() != null)
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputPercent;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerProfilePercent extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
private InputPercent pct = new InputPercent();
|
||||||
|
private Comparator comparator = new Comparator();
|
||||||
|
|
||||||
|
public TriggerProfilePercent() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerProfilePercent(TriggerProfilePercent triggerProfilePercent) {
|
||||||
|
super();
|
||||||
|
pct = new InputPercent(triggerProfilePercent.pct);
|
||||||
|
comparator = new Comparator(triggerProfilePercent.comparator);
|
||||||
|
lastRun = triggerProfilePercent.lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue() {
|
||||||
|
return pct.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLastRun() {
|
||||||
|
return lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Profile profile = ProfileFunctions.getInstance().getProfile();
|
||||||
|
if (profile == null && comparator.getValue().equals(Comparator.Compare.IS_NOT_AVAILABLE)) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (profile == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
boolean doRun = comparator.getValue().check((double) profile.getPercentage(), pct.getValue());
|
||||||
|
if (doRun) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerProfilePercent.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("percentage", pct.getValue());
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
pct.setValue(JsonHelper.safeGetDouble(d, "percentage"));
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.profilepercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.percentagecompared, MainApp.gs(comparator.getValue().getStringRes()), (int) pct.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.icon_actions_profileswitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerProfilePercent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerProfilePercent setValue(double value) {
|
||||||
|
pct.setValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerProfilePercent lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerProfilePercent comparator(Comparator.Compare compare) {
|
||||||
|
this.comparator = new Comparator().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.profilepercentage))
|
||||||
|
.add(comparator)
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.percent_u), "", pct))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,317 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.dpro.widgets.WeekdaysPicker;
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerRecurringTime extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
public enum DayOfWeek {
|
||||||
|
MONDAY,
|
||||||
|
TUESDAY,
|
||||||
|
WEDNESDAY,
|
||||||
|
THURSDAY,
|
||||||
|
FRIDAY,
|
||||||
|
SATURDAY,
|
||||||
|
SUNDAY;
|
||||||
|
|
||||||
|
private static final int[] calendarInts = new int[]{
|
||||||
|
Calendar.MONDAY,
|
||||||
|
Calendar.TUESDAY,
|
||||||
|
Calendar.WEDNESDAY,
|
||||||
|
Calendar.THURSDAY,
|
||||||
|
Calendar.FRIDAY,
|
||||||
|
Calendar.SATURDAY,
|
||||||
|
Calendar.SUNDAY
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final int[] shortNames = new int[]{
|
||||||
|
R.string.weekday_monday_short,
|
||||||
|
R.string.weekday_tuesday_short,
|
||||||
|
R.string.weekday_wednesday_short,
|
||||||
|
R.string.weekday_thursday_short,
|
||||||
|
R.string.weekday_friday_short,
|
||||||
|
R.string.weekday_saturday_short,
|
||||||
|
R.string.weekday_sunday_short
|
||||||
|
};
|
||||||
|
|
||||||
|
public int toCalendarInt() {
|
||||||
|
return calendarInts[ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static DayOfWeek fromCalendarInt(int day) {
|
||||||
|
for (int i = 0; i < calendarInts.length; ++i) {
|
||||||
|
if (calendarInts[i] == day)
|
||||||
|
return values()[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @StringRes
|
||||||
|
int getShortName() {
|
||||||
|
return shortNames[ordinal()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final boolean[] weekdays = new boolean[DayOfWeek.values().length];
|
||||||
|
|
||||||
|
// Recurring
|
||||||
|
private int hour;
|
||||||
|
private int minute;
|
||||||
|
|
||||||
|
private long validTo;
|
||||||
|
|
||||||
|
public TriggerRecurringTime() {
|
||||||
|
super();
|
||||||
|
setAll(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerRecurringTime(TriggerRecurringTime triggerTime) {
|
||||||
|
super();
|
||||||
|
lastRun = triggerTime.lastRun;
|
||||||
|
hour = triggerTime.hour;
|
||||||
|
minute = triggerTime.minute;
|
||||||
|
validTo = triggerTime.validTo;
|
||||||
|
|
||||||
|
if (weekdays.length >= 0)
|
||||||
|
System.arraycopy(triggerTime.weekdays, 0, weekdays, 0, weekdays.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAll(boolean value) {
|
||||||
|
for (DayOfWeek day : DayOfWeek.values()) {
|
||||||
|
set(day, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerRecurringTime set(DayOfWeek day, boolean value) {
|
||||||
|
weekdays[day.ordinal()] = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSet(DayOfWeek day) {
|
||||||
|
return weekdays[day.ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRun() {
|
||||||
|
if (validTo != 0 && DateUtil.now() > validTo)
|
||||||
|
return false;
|
||||||
|
Calendar c = Calendar.getInstance();
|
||||||
|
int scheduledDayOfWeek = c.get(Calendar.DAY_OF_WEEK);
|
||||||
|
|
||||||
|
Calendar scheduledCal = DateUtil.gregorianCalendar();
|
||||||
|
scheduledCal.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
scheduledCal.set(Calendar.MINUTE, minute);
|
||||||
|
scheduledCal.set(Calendar.SECOND, 0);
|
||||||
|
long scheduled = scheduledCal.getTimeInMillis();
|
||||||
|
|
||||||
|
if (isSet(Objects.requireNonNull(DayOfWeek.fromCalendarInt(scheduledDayOfWeek)))) {
|
||||||
|
if (DateUtil.now() >= scheduled && DateUtil.now() - scheduled < T.mins(5).msecs()) {
|
||||||
|
if (lastRun < scheduled) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject object = new JSONObject();
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
try {
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
for (int i = 0; i < weekdays.length; ++i) {
|
||||||
|
data.put(DayOfWeek.values()[i].name(), weekdays[i]);
|
||||||
|
}
|
||||||
|
data.put("hour", hour);
|
||||||
|
data.put("minute", minute);
|
||||||
|
data.put("validTo", validTo);
|
||||||
|
object.put("type", TriggerRecurringTime.class.getName());
|
||||||
|
object.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return object.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
JSONObject o;
|
||||||
|
try {
|
||||||
|
o = new JSONObject(data);
|
||||||
|
lastRun = JsonHelper.safeGetLong(o, "lastRun");
|
||||||
|
for (int i = 0; i < weekdays.length; ++i) {
|
||||||
|
weekdays[i] = JsonHelper.safeGetBoolean(o, DayOfWeek.values()[i].name());
|
||||||
|
}
|
||||||
|
hour = JsonHelper.safeGetInt(o, "hour");
|
||||||
|
minute = JsonHelper.safeGetInt(o, "minute");
|
||||||
|
validTo = JsonHelper.safeGetLong(o, "validTo");
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.recurringTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
int counter = 0;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(MainApp.gs(R.string.every));
|
||||||
|
sb.append(" ");
|
||||||
|
for (Integer i : getSelectedDays()) {
|
||||||
|
if (counter > 0)
|
||||||
|
sb.append(",");
|
||||||
|
sb.append(MainApp.gs(Objects.requireNonNull(DayOfWeek.fromCalendarInt(i)).getShortName()));
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
sb.append(" ");
|
||||||
|
|
||||||
|
Calendar scheduledCal = DateUtil.gregorianCalendar();
|
||||||
|
scheduledCal.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
scheduledCal.set(Calendar.MINUTE, minute);
|
||||||
|
scheduledCal.set(Calendar.SECOND, 0);
|
||||||
|
long scheduled = scheduledCal.getTimeInMillis();
|
||||||
|
|
||||||
|
sb.append(DateUtil.timeString(scheduled));
|
||||||
|
|
||||||
|
if (counter == 0)
|
||||||
|
return MainApp.gs(R.string.never);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_access_alarm_24dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerRecurringTime(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerRecurringTime lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SameParameterValue")
|
||||||
|
TriggerRecurringTime validTo(long validTo) {
|
||||||
|
this.validTo = validTo;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerRecurringTime hour(int hour) {
|
||||||
|
this.hour = hour;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerRecurringTime minute(int minute) {
|
||||||
|
this.minute = minute;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> getSelectedDays() {
|
||||||
|
List<Integer> selectedDays = new ArrayList<>();
|
||||||
|
for (int i = 0; i < weekdays.length; ++i) {
|
||||||
|
DayOfWeek day = DayOfWeek.values()[i];
|
||||||
|
boolean selected = weekdays[i];
|
||||||
|
if (selected) selectedDays.add(day.toCalendarInt());
|
||||||
|
}
|
||||||
|
return selectedDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
TextView label = new TextView(root.getContext());
|
||||||
|
|
||||||
|
// TODO: Replace external tool WeekdaysPicker with a self-made GUI element
|
||||||
|
WeekdaysPicker weekdaysPicker = new WeekdaysPicker(root.getContext());
|
||||||
|
weekdaysPicker.setEditable(true);
|
||||||
|
weekdaysPicker.setSelectedDays(getSelectedDays());
|
||||||
|
weekdaysPicker.setOnWeekdaysChangeListener((view, i, list) -> set(DayOfWeek.fromCalendarInt(i), list.contains(i)));
|
||||||
|
weekdaysPicker.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
root.addView(weekdaysPicker);
|
||||||
|
|
||||||
|
TextView timeButton = new TextView(root.getContext());
|
||||||
|
|
||||||
|
GregorianCalendar runAt = new GregorianCalendar();
|
||||||
|
//Date runAt = new Date();
|
||||||
|
runAt.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
runAt.set(Calendar.MINUTE, minute);
|
||||||
|
timeButton.setText(DateUtil.timeString(runAt.getTimeInMillis()));
|
||||||
|
timeButton.setOnClickListener(view -> {
|
||||||
|
TimePickerDialog tpd = TimePickerDialog.newInstance(
|
||||||
|
(view12, hourOfDay, minute, second) -> {
|
||||||
|
hour(hourOfDay);
|
||||||
|
minute(minute);
|
||||||
|
runAt.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
runAt.set(Calendar.MINUTE, minute);
|
||||||
|
timeButton.setText(DateUtil.timeString(runAt.getTimeInMillis()));
|
||||||
|
},
|
||||||
|
runAt.get(Calendar.HOUR_OF_DAY),
|
||||||
|
runAt.get(Calendar.MINUTE),
|
||||||
|
DateFormat.is24HourFormat(root.getContext())
|
||||||
|
);
|
||||||
|
tpd.setThemeDark(true);
|
||||||
|
tpd.dismissOnPause(true);
|
||||||
|
Activity a = scanForActivity(root.getContext());
|
||||||
|
if (a != null)
|
||||||
|
tpd.show(a.getFragmentManager(), "TimePickerDialog");
|
||||||
|
});
|
||||||
|
|
||||||
|
int px = MainApp.dpToPx(10);
|
||||||
|
label.setText(MainApp.gs(R.string.atspecifiedtime, ""));
|
||||||
|
label.setTypeface(label.getTypeface(), Typeface.BOLD);
|
||||||
|
label.setPadding(px, px, px, px);
|
||||||
|
timeButton.setPadding(px, px, px, px);
|
||||||
|
|
||||||
|
LinearLayout l = new LinearLayout(root.getContext());
|
||||||
|
l.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
l.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
l.addView(label);
|
||||||
|
l.addView(timeButton);
|
||||||
|
root.addView(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.db.TempTarget;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.ComparatorExists;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerTempTarget extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
private ComparatorExists comparator = new ComparatorExists();
|
||||||
|
|
||||||
|
public TriggerTempTarget() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerTempTarget(TriggerTempTarget triggerTempTarget) {
|
||||||
|
super();
|
||||||
|
comparator = new ComparatorExists(triggerTempTarget.comparator);
|
||||||
|
lastRun = triggerTempTarget.lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComparatorExists getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
TempTarget tt = TreatmentsPlugin.getPlugin().getTempTargetFromHistory();
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (tt == null && comparator.getValue() == ComparatorExists.Compare.NOT_EXISTS) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tt != null && comparator.getValue() == ComparatorExists.Compare.EXISTS) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerTempTarget.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(ComparatorExists.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.temptarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.temptargetcompared, MainApp.gs(comparator.getValue().getStringRes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_keyboard_tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerTempTarget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerTempTarget lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TriggerTempTarget comparator(ComparatorExists.Compare compare) {
|
||||||
|
this.comparator = new ComparatorExists().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.temptarget))
|
||||||
|
.add(comparator)
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.text.format.DateFormat;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
|
||||||
|
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerTime extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
private long runAt;
|
||||||
|
|
||||||
|
public TriggerTime() {
|
||||||
|
runAt = DateUtil.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerTime(TriggerTime triggerTime) {
|
||||||
|
super();
|
||||||
|
lastRun = triggerTime.lastRun;
|
||||||
|
runAt = triggerTime.runAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldRun() {
|
||||||
|
long now = DateUtil.now();
|
||||||
|
if (now >= runAt && now - runAt < T.mins(5).msecs())
|
||||||
|
if (lastRun < runAt) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toJSON() {
|
||||||
|
JSONObject object = new JSONObject();
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
try {
|
||||||
|
data.put("runAt", runAt);
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
object.put("type", TriggerTime.class.getName());
|
||||||
|
object.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return object.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
JSONObject o;
|
||||||
|
try {
|
||||||
|
o = new JSONObject(data);
|
||||||
|
lastRun = JsonHelper.safeGetLong(o, "lastRun");
|
||||||
|
runAt = JsonHelper.safeGetLong(o, "runAt");
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.atspecifiedtime, DateUtil.dateAndTimeString(runAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_access_alarm_24dp);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerTime runAt(long runAt) {
|
||||||
|
this.runAt = runAt;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerTime lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerTime(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
long getRunAt() {
|
||||||
|
return runAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
TextView label = new TextView(root.getContext());
|
||||||
|
TextView dateButton = new TextView(root.getContext());
|
||||||
|
TextView timeButton = new TextView(root.getContext());
|
||||||
|
|
||||||
|
dateButton.setText(DateUtil.dateString(runAt));
|
||||||
|
timeButton.setText(DateUtil.timeString(runAt));
|
||||||
|
dateButton.setOnClickListener(view -> {
|
||||||
|
GregorianCalendar calendar = new GregorianCalendar();
|
||||||
|
calendar.setTimeInMillis(runAt);
|
||||||
|
DatePickerDialog dpd = DatePickerDialog.newInstance(
|
||||||
|
(view1, year, monthOfYear, dayOfMonth) -> {
|
||||||
|
calendar.set(Calendar.YEAR, year);
|
||||||
|
calendar.set(Calendar.MONTH, monthOfYear);
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||||
|
runAt = calendar.getTimeInMillis();
|
||||||
|
dateButton.setText(DateUtil.dateString(runAt));
|
||||||
|
},
|
||||||
|
calendar.get(Calendar.YEAR),
|
||||||
|
calendar.get(Calendar.MONTH),
|
||||||
|
calendar.get(Calendar.DAY_OF_MONTH)
|
||||||
|
);
|
||||||
|
dpd.setThemeDark(true);
|
||||||
|
dpd.dismissOnPause(true);
|
||||||
|
Activity a = scanForActivity(root.getContext());
|
||||||
|
if (a != null)
|
||||||
|
dpd.show(a.getFragmentManager(), "DatePickerDialog");
|
||||||
|
});
|
||||||
|
timeButton.setOnClickListener(view -> {
|
||||||
|
GregorianCalendar calendar = new GregorianCalendar();
|
||||||
|
calendar.setTimeInMillis(runAt);
|
||||||
|
TimePickerDialog tpd = TimePickerDialog.newInstance(
|
||||||
|
(view12, hourOfDay, minute, second) -> {
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
|
||||||
|
calendar.set(Calendar.MINUTE, minute);
|
||||||
|
runAt = calendar.getTimeInMillis();
|
||||||
|
timeButton.setText(DateUtil.timeString(runAt));
|
||||||
|
},
|
||||||
|
calendar.get(Calendar.HOUR_OF_DAY),
|
||||||
|
calendar.get(Calendar.MINUTE),
|
||||||
|
DateFormat.is24HourFormat(root.getContext())
|
||||||
|
);
|
||||||
|
tpd.setThemeDark(true);
|
||||||
|
tpd.dismissOnPause(true);
|
||||||
|
Activity a = scanForActivity(root.getContext());
|
||||||
|
if (a != null)
|
||||||
|
tpd.show(a.getFragmentManager(), "TimePickerDialog");
|
||||||
|
});
|
||||||
|
|
||||||
|
int px = MainApp.dpToPx(10);
|
||||||
|
label.setText(MainApp.gs(R.string.atspecifiedtime, ""));
|
||||||
|
label.setTypeface(label.getTypeface(), Typeface.BOLD);
|
||||||
|
label.setPadding(px, px, px, px);
|
||||||
|
dateButton.setPadding(px, px, px, px);
|
||||||
|
timeButton.setPadding(px, px, px, px);
|
||||||
|
|
||||||
|
LinearLayout l = new LinearLayout(root.getContext());
|
||||||
|
l.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
l.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
|
||||||
|
l.addView(label);
|
||||||
|
l.addView(dateButton);
|
||||||
|
l.addView(timeButton);
|
||||||
|
root.addView(l);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package info.nightscout.androidaps.plugins.general.automation.triggers;
|
||||||
|
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.events.EventNetworkChange;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.InputString;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LabelWithElement;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder;
|
||||||
|
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel;
|
||||||
|
import info.nightscout.androidaps.receivers.NetworkChangeReceiver;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.JsonHelper;
|
||||||
|
import info.nightscout.androidaps.utils.T;
|
||||||
|
|
||||||
|
public class TriggerWifiSsid extends Trigger {
|
||||||
|
private static Logger log = LoggerFactory.getLogger(L.AUTOMATION);
|
||||||
|
|
||||||
|
private InputString ssid = new InputString();
|
||||||
|
private Comparator comparator = new Comparator();
|
||||||
|
|
||||||
|
public TriggerWifiSsid() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TriggerWifiSsid(TriggerWifiSsid triggerWifiSsid) {
|
||||||
|
super();
|
||||||
|
ssid = new InputString(triggerWifiSsid.ssid);
|
||||||
|
comparator = new Comparator(triggerWifiSsid.comparator);
|
||||||
|
lastRun = triggerWifiSsid.lastRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return ssid.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Comparator getComparator() {
|
||||||
|
return comparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean shouldRun() {
|
||||||
|
EventNetworkChange eventNetworkChange = NetworkChangeReceiver.getLastEvent();
|
||||||
|
if (eventNetworkChange == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (lastRun > DateUtil.now() - T.mins(5).msecs())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!eventNetworkChange.wifiConnected && comparator.getValue() == Comparator.Compare.IS_NOT_AVAILABLE) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean doRun = eventNetworkChange.wifiConnected && comparator.getValue().check(eventNetworkChange.ssid, getValue());
|
||||||
|
if (doRun) {
|
||||||
|
if (L.isEnabled(L.AUTOMATION))
|
||||||
|
log.debug("Ready for execution: " + friendlyDescription());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toJSON() {
|
||||||
|
JSONObject o = new JSONObject();
|
||||||
|
try {
|
||||||
|
o.put("type", TriggerWifiSsid.class.getName());
|
||||||
|
JSONObject data = new JSONObject();
|
||||||
|
data.put("ssid", getValue());
|
||||||
|
data.put("lastRun", lastRun);
|
||||||
|
data.put("comparator", comparator.getValue().toString());
|
||||||
|
o.put("data", data);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return o.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Trigger fromJSON(String data) {
|
||||||
|
try {
|
||||||
|
JSONObject d = new JSONObject(data);
|
||||||
|
ssid.setValue(JsonHelper.safeGetString(d, "ssid"));
|
||||||
|
lastRun = JsonHelper.safeGetLong(d, "lastRun");
|
||||||
|
comparator.setValue(Comparator.Compare.valueOf(JsonHelper.safeGetString(d, "comparator")));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int friendlyName() {
|
||||||
|
return R.string.ns_wifi_ssids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String friendlyDescription() {
|
||||||
|
return MainApp.gs(R.string.wifissidcompared, MainApp.gs(comparator.getValue().getStringRes()), getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Integer> icon() {
|
||||||
|
return Optional.of(R.drawable.ic_network_wifi);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Trigger duplicate() {
|
||||||
|
return new TriggerWifiSsid(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerWifiSsid setValue(String value) {
|
||||||
|
ssid.setValue(value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerWifiSsid lastRun(long lastRun) {
|
||||||
|
this.lastRun = lastRun;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TriggerWifiSsid comparator(Comparator.Compare compare) {
|
||||||
|
this.comparator = new Comparator().setValue(compare);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generateDialog(LinearLayout root, FragmentManager fragmentManager) {
|
||||||
|
new LayoutBuilder()
|
||||||
|
.add(new StaticLabel(R.string.ns_wifi_ssids))
|
||||||
|
.add(comparator)
|
||||||
|
.add(new LabelWithElement(MainApp.gs(R.string.ns_wifi_ssids) + ": ", "", ssid))
|
||||||
|
.build(root);
|
||||||
|
}
|
||||||
|
}
|
|
@ -266,14 +266,14 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
|
||||||
editBg = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_bginput);
|
editBg = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_bginput);
|
||||||
editTemptarget = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_temptarget);
|
editTemptarget = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_temptarget);
|
||||||
if (profile == null) {
|
if (profile == null) {
|
||||||
editBg.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, bgTextWatcher);
|
editBg.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok), bgTextWatcher);
|
||||||
editTemptarget.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false);
|
editTemptarget.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok));
|
||||||
} else if (units.equals(Constants.MMOL)) {
|
} else if (units.equals(Constants.MMOL)) {
|
||||||
editBg.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, bgTextWatcher);
|
editBg.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok), bgTextWatcher);
|
||||||
editTemptarget.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false);
|
editTemptarget.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok));
|
||||||
} else {
|
} else {
|
||||||
editBg.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, bgTextWatcher);
|
editBg.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), bgTextWatcher);
|
||||||
editTemptarget.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false);
|
editTemptarget.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
}
|
}
|
||||||
|
|
||||||
sensorRadioButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
sensorRadioButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||||
|
@ -287,16 +287,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
|
||||||
|
|
||||||
Integer maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value();
|
Integer maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value();
|
||||||
editCarbs = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_carbsinput);
|
editCarbs = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_carbsinput);
|
||||||
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false);
|
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
Double maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
Double maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
||||||
editInsulin = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_insulininput);
|
editInsulin = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_insulininput);
|
||||||
editInsulin.setParams(0d, 0d, maxInsulin, 0.05d, new DecimalFormat("0.00"), false);
|
editInsulin.setParams(0d, 0d, maxInsulin, 0.05d, new DecimalFormat("0.00"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
editSplit = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_splitinput);
|
editSplit = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_splitinput);
|
||||||
editSplit.setParams(100d, 0d, 100d, 5d, new DecimalFormat("0"), true);
|
editSplit.setParams(100d, 0d, 100d, 5d, new DecimalFormat("0"), true, view.findViewById(R.id.ok));
|
||||||
editDuration = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_durationinput);
|
editDuration = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_durationinput);
|
||||||
editDuration.setParams(0d, 0d, 24 * 60d, 10d, new DecimalFormat("0"), false);
|
editDuration.setParams(0d, 0d, 24 * 60d, 10d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
TextWatcher percentTextWatcher = new TextWatcher() {
|
TextWatcher percentTextWatcher = new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -320,7 +320,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
|
||||||
if (profile != null)
|
if (profile != null)
|
||||||
maxPercent = MainApp.getConstraintChecker().getMaxBasalPercentAllowed(profile).value();
|
maxPercent = MainApp.getConstraintChecker().getMaxBasalPercentAllowed(profile).value();
|
||||||
editPercent = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_percentinput);
|
editPercent = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_percentinput);
|
||||||
editPercent.setParams(0d, -100d, (double) maxPercent, 5d, new DecimalFormat("0"), true, percentTextWatcher);
|
editPercent.setParams(0d, -100d, (double) maxPercent, 5d, new DecimalFormat("0"), true, view.findViewById(R.id.ok), percentTextWatcher);
|
||||||
|
|
||||||
TextWatcher absoluteTextWatcher = new TextWatcher() {
|
TextWatcher absoluteTextWatcher = new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -344,16 +344,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
|
||||||
if (profile != null)
|
if (profile != null)
|
||||||
maxAbsolute = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value();
|
maxAbsolute = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value();
|
||||||
editAbsolute = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_absoluteinput);
|
editAbsolute = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_absoluteinput);
|
||||||
editAbsolute.setParams(0d, 0d, maxAbsolute, 0.05d, new DecimalFormat("0.00"), true, absoluteTextWatcher);
|
editAbsolute.setParams(0d, 0d, maxAbsolute, 0.05d, new DecimalFormat("0.00"), true, view.findViewById(R.id.ok), absoluteTextWatcher);
|
||||||
|
|
||||||
editCarbTime = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_carbtimeinput);
|
editCarbTime = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_carbtimeinput);
|
||||||
editCarbTime.setParams(0d, -60d, 60d, 5d, new DecimalFormat("0"), false);
|
editCarbTime.setParams(0d, -60d, 60d, 5d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
editPercentage = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_percentage);
|
editPercentage = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_percentage);
|
||||||
editPercentage.setParams(100d, (double) Constants.CPP_MIN_PERCENTAGE, (double) Constants.CPP_MAX_PERCENTAGE, 1d, new DecimalFormat("0"), false);
|
editPercentage.setParams(100d, (double) Constants.CPP_MIN_PERCENTAGE, (double) Constants.CPP_MAX_PERCENTAGE, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
editTimeshift = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_timeshift);
|
editTimeshift = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_timeshift);
|
||||||
editTimeshift.setParams(0d, (double) Constants.CPP_MIN_TIMESHIFT, (double) Constants.CPP_MAX_TIMESHIFT, 1d, new DecimalFormat("0"), false);
|
editTimeshift.setParams(0d, (double) Constants.CPP_MIN_TIMESHIFT, (double) Constants.CPP_MAX_TIMESHIFT, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
ProfileSwitch ps = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now());
|
ProfileSwitch ps = TreatmentsPlugin.getPlugin().getProfileSwitchFromHistory(DateUtil.now());
|
||||||
if (ps != null && ps.isCPP) {
|
if (ps != null && ps.isCPP) {
|
||||||
|
|
|
@ -66,9 +66,9 @@ public class CalibrationDialog extends DialogFragment implements View.OnClickLis
|
||||||
bgNumber = (NumberPicker) view.findViewById(R.id.overview_calibration_bg);
|
bgNumber = (NumberPicker) view.findViewById(R.id.overview_calibration_bg);
|
||||||
|
|
||||||
if (units.equals(Constants.MMOL))
|
if (units.equals(Constants.MMOL))
|
||||||
bgNumber.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false);
|
bgNumber.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok));
|
||||||
else
|
else
|
||||||
bgNumber.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false);
|
bgNumber.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok));
|
||||||
|
|
||||||
unitsView = (TextView) view.findViewById(R.id.overview_calibration_units);
|
unitsView = (TextView) view.findViewById(R.id.overview_calibration_units);
|
||||||
unitsView.setText(units);
|
unitsView.setText(units);
|
||||||
|
|
|
@ -130,15 +130,15 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C
|
||||||
startHypoTTCheckbox = view.findViewById(R.id.newcarbs_hypo_tt);
|
startHypoTTCheckbox = view.findViewById(R.id.newcarbs_hypo_tt);
|
||||||
|
|
||||||
editTime = view.findViewById(R.id.newcarbs_time);
|
editTime = view.findViewById(R.id.newcarbs_time);
|
||||||
editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, textWatcher);
|
editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
|
|
||||||
editDuration = view.findViewById(R.id.new_carbs_duration);
|
editDuration = view.findViewById(R.id.new_carbs_duration);
|
||||||
editDuration.setParams(0d, 0d, 10d, 1d, new DecimalFormat("0"), false, textWatcher);
|
editDuration.setParams(0d, 0d, 10d, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
|
|
||||||
maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value();
|
maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value();
|
||||||
|
|
||||||
editCarbs = view.findViewById(R.id.newcarb_carbsamount);
|
editCarbs = view.findViewById(R.id.newcarb_carbsamount);
|
||||||
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, textWatcher);
|
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
|
|
||||||
Button fav1Button = view.findViewById(R.id.newcarbs_plus1);
|
Button fav1Button = view.findViewById(R.id.newcarbs_plus1);
|
||||||
fav1Button.setOnClickListener(this);
|
fav1Button.setOnClickListener(this);
|
||||||
|
|
|
@ -126,12 +126,12 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener
|
||||||
editLayout = view.findViewById(R.id.newinsulin_time_layout);
|
editLayout = view.findViewById(R.id.newinsulin_time_layout);
|
||||||
editLayout.setVisibility(View.GONE);
|
editLayout.setVisibility(View.GONE);
|
||||||
editTime = view.findViewById(R.id.newinsulin_time);
|
editTime = view.findViewById(R.id.newinsulin_time);
|
||||||
editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, textWatcher);
|
editTime.setParams(0d, -12 * 60d, 12 * 60d, 5d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
|
|
||||||
maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
maxInsulin = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
||||||
|
|
||||||
editInsulin = view.findViewById(R.id.newinsulin_amount);
|
editInsulin = view.findViewById(R.id.newinsulin_amount);
|
||||||
editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, textWatcher);
|
editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
|
|
||||||
Button plus1Button = view.findViewById(R.id.newinsulin_plus05);
|
Button plus1Button = view.findViewById(R.id.newinsulin_plus05);
|
||||||
plus1Button.setOnClickListener(this);
|
plus1Button.setOnClickListener(this);
|
||||||
|
@ -226,7 +226,7 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Math.abs(insulinAfterConstraints - insulin) > pump.getPumpDescription().pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
|
if (Math.abs(insulinAfterConstraints - insulin) > pump.getPumpDescription().pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
|
||||||
actions.add("<font color='" + MainApp.gc(R.color.warning) + "'>" + MainApp.gs(R.string.bolusconstraintapplied) + "</font>");
|
actions.add(MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints));
|
||||||
|
|
||||||
int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration);
|
int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration);
|
||||||
eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration;
|
eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration;
|
||||||
|
|
|
@ -101,8 +101,8 @@ public class NewTreatmentDialog extends DialogFragment implements OnClickListene
|
||||||
editCarbs = (NumberPicker) view.findViewById(R.id.treatments_newtreatment_carbsamount);
|
editCarbs = (NumberPicker) view.findViewById(R.id.treatments_newtreatment_carbsamount);
|
||||||
editInsulin = (NumberPicker) view.findViewById(R.id.treatments_newtreatment_insulinamount);
|
editInsulin = (NumberPicker) view.findViewById(R.id.treatments_newtreatment_insulinamount);
|
||||||
|
|
||||||
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, textWatcher);
|
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, textWatcher);
|
editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
|
|
||||||
recordOnlyCheckbox = (CheckBox) view.findViewById(R.id.newtreatment_record_only);
|
recordOnlyCheckbox = (CheckBox) view.findViewById(R.id.newtreatment_record_only);
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ public class NewTreatmentDialog extends DialogFragment implements OnClickListene
|
||||||
confirmMessage += "<br/><font color='" + MainApp.gc(R.color.warning) + "'>" + MainApp.gs(R.string.bolusrecordedonly) + "</font>";
|
confirmMessage += "<br/><font color='" + MainApp.gc(R.color.warning) + "'>" + MainApp.gs(R.string.bolusrecordedonly) + "</font>";
|
||||||
}
|
}
|
||||||
if (Math.abs(insulinAfterConstraints - insulin) > pump.getPumpDescription().pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) || !Objects.equals(carbsAfterConstraints, carbs))
|
if (Math.abs(insulinAfterConstraints - insulin) > pump.getPumpDescription().pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) || !Objects.equals(carbsAfterConstraints, carbs))
|
||||||
confirmMessage += "<br/><font color='" + MainApp.gc(R.color.warning) + "'>" + MainApp.gs(R.string.bolusconstraintapplied) + "</font>";
|
confirmMessage += "<br/>" + MainApp.gs(R.string.bolusconstraintappliedwarning, MainApp.gc(R.color.warning), insulin, insulinAfterConstraints);
|
||||||
}
|
}
|
||||||
if (carbsAfterConstraints > 0)
|
if (carbsAfterConstraints > 0)
|
||||||
confirmMessage += "<br/>" + MainApp.gs(R.string.carbs) + ": " + "<font color='" + MainApp.gc(R.color.carbs) + "'>" + carbsAfterConstraints + "g" + "</font>";
|
confirmMessage += "<br/>" + MainApp.gs(R.string.carbs) + ": " + "<font color='" + MainApp.gc(R.color.carbs) + "'>" + carbsAfterConstraints + "g" + "</font>";
|
||||||
|
|
|
@ -238,11 +238,11 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com
|
||||||
Integer maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value();
|
Integer maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value();
|
||||||
Double maxCorrection = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
Double maxCorrection = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
||||||
|
|
||||||
editBg.setParams(0d, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, textWatcher);
|
editBg.setParams(0d, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, textWatcher);
|
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
double bolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep;
|
double bolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep;
|
||||||
editCorr.setParams(0d, -maxCorrection, maxCorrection, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false, textWatcher);
|
editCorr.setParams(0d, -maxCorrection, maxCorrection, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
editCarbTime.setParams(0d, -60d, 60d, 5d, new DecimalFormat("0"), false);
|
editCarbTime.setParams(0d, -60d, 60d, 5d, new DecimalFormat("0"), false, view.findViewById(R.id.ok), textWatcher);
|
||||||
initDialog();
|
initDialog();
|
||||||
|
|
||||||
setCancelable(true);
|
setCancelable(true);
|
||||||
|
@ -392,7 +392,9 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com
|
||||||
c_cob = cobInfo.displayCob;
|
c_cob = cobInfo.displayCob;
|
||||||
}
|
}
|
||||||
|
|
||||||
wizard = new BolusWizard(specificProfile, profileName, tempTarget, carbsAfterConstraint, c_cob, c_bg, corrAfterConstraint, 100d, bgCheckbox.isChecked(), cobCheckbox.isChecked(), bolusIobCheckbox.isChecked(), basalIobCheckbox.isChecked(), superbolusCheckbox.isChecked(), ttCheckbox.isChecked(), bgtrendCheckbox.isChecked(), notesEdit.getText().toString(), SafeParse.stringToInt(editCarbTime.getText()));
|
int carbTime = SafeParse.stringToInt(editCarbTime.getText());
|
||||||
|
|
||||||
|
wizard = new BolusWizard(specificProfile, profileName, tempTarget, carbsAfterConstraint, c_cob, c_bg, corrAfterConstraint, 100d, bgCheckbox.isChecked(), cobCheckbox.isChecked(), bolusIobCheckbox.isChecked(), basalIobCheckbox.isChecked(), superbolusCheckbox.isChecked(), ttCheckbox.isChecked(), bgtrendCheckbox.isChecked(), notesEdit.getText().toString(), carbTime);
|
||||||
|
|
||||||
bg.setText(c_bg + " ISF: " + DecimalFormatter.to1Decimal(wizard.getSens()));
|
bg.setText(c_bg + " ISF: " + DecimalFormatter.to1Decimal(wizard.getSens()));
|
||||||
bgInsulin.setText(StringUtils.formatInsulin(wizard.getInsulinFromBG()));
|
bgInsulin.setText(StringUtils.formatInsulin(wizard.getInsulinFromBG()));
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
package info.nightscout.androidaps.plugins.general.overview.notifications;
|
package info.nightscout.androidaps.plugins.general.overview.notifications;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -75,6 +74,8 @@ public class Notification {
|
||||||
public static final int DST_IN_24H = 50;
|
public static final int DST_IN_24H = 50;
|
||||||
public static final int DISKFULL = 51;
|
public static final int DISKFULL = 51;
|
||||||
public static final int OLDVERSION = 52;
|
public static final int OLDVERSION = 52;
|
||||||
|
public static final int USERMESSAGE = 53;
|
||||||
|
public static final int OVER_24H_TIME_CHANGE_REQUESTED = 54;
|
||||||
|
|
||||||
|
|
||||||
public int id;
|
public int id;
|
||||||
|
@ -85,6 +86,7 @@ public class Notification {
|
||||||
|
|
||||||
public NSAlarm nsAlarm = null;
|
public NSAlarm nsAlarm = null;
|
||||||
public Integer soundId = null;
|
public Integer soundId = null;
|
||||||
|
|
||||||
public Notification() {
|
public Notification() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -790,7 +790,7 @@ public class SmsCommunicatorPlugin extends PluginBase {
|
||||||
passCode += Character.toString((char) (startChar2 + Math.random() * ('z' - 'a' + 1)));
|
passCode += Character.toString((char) (startChar2 + Math.random() * ('z' - 'a' + 1)));
|
||||||
int startChar3 = Math.random() > 0.5 ? 'a' : 'A';
|
int startChar3 = Math.random() > 0.5 ? 'a' : 'A';
|
||||||
passCode += Character.toString((char) (startChar3 + Math.random() * ('z' - 'a' + 1)));
|
passCode += Character.toString((char) (startChar3 + Math.random() * ('z' - 'a' + 1)));
|
||||||
passCode.replace('l', 'k').replace('I', 'J');
|
passCode = passCode.replace('l', 'k').replace('I', 'J');
|
||||||
return passCode;
|
return passCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -518,6 +518,20 @@ public class IobCobCalculatorPlugin extends PluginBase {
|
||||||
return new CobInfo(displayCob, futureCarbs);
|
return new CobInfo(displayCob, futureCarbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public double slowAbsorptionPercentage(int timeInMinutes) {
|
||||||
|
double sum = 0;
|
||||||
|
int count = 0;
|
||||||
|
int valuesToProcess = timeInMinutes / 5;
|
||||||
|
synchronized (dataLock) {
|
||||||
|
for (int i = autosensDataTable.size() - 1; i >= 0 && count < valuesToProcess; i--) {
|
||||||
|
if (autosensDataTable.valueAt(i).failoverToMinAbsorbtionRate)
|
||||||
|
sum++;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum /count;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public AutosensData getLastAutosensData(String reason) {
|
public AutosensData getLastAutosensData(String reason) {
|
||||||
if (autosensDataTable.size() < 1) {
|
if (autosensDataTable.size() < 1) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class LocalProfileFragment extends SubscriberFragment {
|
||||||
PumpDescription pumpDescription = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription();
|
PumpDescription pumpDescription = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription();
|
||||||
View layout = inflater.inflate(R.layout.localprofile_fragment, container, false);
|
View layout = inflater.inflate(R.layout.localprofile_fragment, container, false);
|
||||||
diaView = (NumberPicker) layout.findViewById(R.id.localprofile_dia);
|
diaView = (NumberPicker) layout.findViewById(R.id.localprofile_dia);
|
||||||
diaView.setParams(LocalProfilePlugin.getPlugin().dia, 2d, 48d, 0.1d, new DecimalFormat("0.0"), false, textWatch);
|
diaView.setParams(LocalProfilePlugin.getPlugin().dia, 2d, 48d, 0.1d, new DecimalFormat("0.0"), false, layout.findViewById(R.id.localprofile_save), textWatch);
|
||||||
mgdlView = (RadioButton) layout.findViewById(R.id.localprofile_mgdl);
|
mgdlView = (RadioButton) layout.findViewById(R.id.localprofile_mgdl);
|
||||||
mmolView = (RadioButton) layout.findViewById(R.id.localprofile_mmol);
|
mmolView = (RadioButton) layout.findViewById(R.id.localprofile_mmol);
|
||||||
icView = new TimeListEdit(getContext(), layout, R.id.localprofile_ic, MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.getPlugin().ic, null, 0.5, 50d, 0.1d, new DecimalFormat("0.0"), save);
|
icView = new TimeListEdit(getContext(), layout, R.id.localprofile_ic, MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.getPlugin().ic, null, 0.5, 50d, 0.1d, new DecimalFormat("0.0"), save);
|
||||||
|
@ -124,7 +124,7 @@ public class LocalProfileFragment extends SubscriberFragment {
|
||||||
LocalProfilePlugin.getPlugin().loadSettings();
|
LocalProfilePlugin.getPlugin().loadSettings();
|
||||||
mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl);
|
mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl);
|
||||||
mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol);
|
mmolView.setChecked(LocalProfilePlugin.getPlugin().mmol);
|
||||||
diaView.setParams(LocalProfilePlugin.getPlugin().dia, 5d, 12d, 0.1d, new DecimalFormat("0.0"), false, textWatch);
|
diaView.setParams(LocalProfilePlugin.getPlugin().dia, 5d, 12d, 0.1d, new DecimalFormat("0.0"), false, view.findViewById(R.id.localprofile_save), textWatch);
|
||||||
icView = new TimeListEdit(getContext(), layout, R.id.localprofile_ic, MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.getPlugin().ic, null, 0.5, 50d, 0.1d, new DecimalFormat("0.0"), save);
|
icView = new TimeListEdit(getContext(), layout, R.id.localprofile_ic, MainApp.gs(R.string.nsprofileview_ic_label) + ":", LocalProfilePlugin.getPlugin().ic, null, 0.5, 50d, 0.1d, new DecimalFormat("0.0"), save);
|
||||||
isfView = new TimeListEdit(getContext(), layout, R.id.localprofile_isf, MainApp.gs(R.string.nsprofileview_isf_label) + ":", LocalProfilePlugin.getPlugin().isf, null, 0.5, 500d, 0.1d, new DecimalFormat("0.0"), save);
|
isfView = new TimeListEdit(getContext(), layout, R.id.localprofile_isf, MainApp.gs(R.string.nsprofileview_isf_label) + ":", LocalProfilePlugin.getPlugin().isf, null, 0.5, 500d, 0.1d, new DecimalFormat("0.0"), save);
|
||||||
basalView = new TimeListEdit(getContext(), layout, R.id.localprofile_basal, MainApp.gs(R.string.nsprofileview_basal_label) + ": " + getSumLabel(), LocalProfilePlugin.getPlugin().basal, null, pumpDescription.basalMinimumRate, 10, 0.01d, new DecimalFormat("0.00"), save);
|
basalView = new TimeListEdit(getContext(), layout, R.id.localprofile_basal, MainApp.gs(R.string.nsprofileview_basal_label) + ": " + getSumLabel(), LocalProfilePlugin.getPlugin().basal, null, pumpDescription.basalMinimumRate, 10, 0.01d, new DecimalFormat("0.00"), save);
|
||||||
|
|
|
@ -39,6 +39,7 @@ import info.nightscout.androidaps.interfaces.PluginType;
|
||||||
import info.nightscout.androidaps.interfaces.PumpDescription;
|
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||||
import info.nightscout.androidaps.interfaces.PumpInterface;
|
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||||
import info.nightscout.androidaps.logging.L;
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.common.ManufacturerType;
|
||||||
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment;
|
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment;
|
||||||
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
|
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin;
|
||||||
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||||
|
@ -163,32 +164,9 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) {
|
public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity activity) {
|
||||||
boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false);
|
confirmPumpPluginActivation(pluginSwitcher, activity);
|
||||||
if (allowHardwarePump || context == null) {
|
|
||||||
pluginSwitcher.invoke();
|
|
||||||
} else {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
||||||
builder.setMessage(R.string.allow_hardware_pump_text)
|
|
||||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
pluginSwitcher.invoke();
|
|
||||||
SP.putBoolean("allow_hardware_pump", true);
|
|
||||||
if (L.isEnabled(L.PUMP))
|
|
||||||
log.debug("First time HW pump allowed!");
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
pluginSwitcher.cancel();
|
|
||||||
if (L.isEnabled(L.PUMP))
|
|
||||||
log.debug("User does not allow switching to HW pump!");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.create().show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInitialized() {
|
public boolean isInitialized() {
|
||||||
|
@ -1313,13 +1291,13 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String manufacter() {
|
public ManufacturerType manufacturer() {
|
||||||
return "Roche";
|
return ManufacturerType.Roche;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String model() {
|
public PumpType model() {
|
||||||
return "Combo";
|
return PumpType.AccuChekCombo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1407,4 +1385,10 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void timeDateOrTimeZoneChanged() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,452 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
|
||||||
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.BuildConfig;
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||||
|
import info.nightscout.androidaps.data.Profile;
|
||||||
|
import info.nightscout.androidaps.data.PumpEnactResult;
|
||||||
|
import info.nightscout.androidaps.db.ExtendedBolus;
|
||||||
|
import info.nightscout.androidaps.db.TemporaryBasal;
|
||||||
|
import info.nightscout.androidaps.events.EventAppExit;
|
||||||
|
import info.nightscout.androidaps.interfaces.ConstraintsInterface;
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginBase;
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginDescription;
|
||||||
|
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||||
|
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpDriverState;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
|
||||||
|
import info.nightscout.androidaps.plugins.treatments.Treatment;
|
||||||
|
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil;
|
||||||
|
import info.nightscout.androidaps.utils.DecimalFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 23.04.18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// When using this class, make sure that your first step is to create mConnection (see MedtronicPumpPlugin)
|
||||||
|
|
||||||
|
public abstract class PumpPluginAbstract extends PluginBase implements PumpInterface, ConstraintsInterface {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
|
||||||
|
|
||||||
|
protected static final PumpEnactResult OPERATION_NOT_SUPPORTED = new PumpEnactResult().success(false)
|
||||||
|
.enacted(false).comment(MainApp.gs(R.string.pump_operation_not_supported_by_pump_driver));
|
||||||
|
protected static final PumpEnactResult OPERATION_NOT_YET_SUPPORTED = new PumpEnactResult().success(false)
|
||||||
|
.enacted(false).comment(MainApp.gs(R.string.pump_operation_not_yet_supported_by_pump));
|
||||||
|
|
||||||
|
protected PumpDescription pumpDescription = new PumpDescription();
|
||||||
|
protected PumpStatus pumpStatus;
|
||||||
|
protected ServiceConnection serviceConnection = null;
|
||||||
|
protected boolean serviceRunning = false;
|
||||||
|
// protected boolean isInitialized = false;
|
||||||
|
protected PumpDriverState pumpState = PumpDriverState.NotInitialized;
|
||||||
|
protected boolean displayConnectionMessages = false;
|
||||||
|
|
||||||
|
|
||||||
|
protected PumpPluginAbstract(PluginDescription pluginDescription, PumpType pumpType) {
|
||||||
|
|
||||||
|
super(pluginDescription);
|
||||||
|
|
||||||
|
pumpDescription.setPumpDescription(pumpType);
|
||||||
|
|
||||||
|
initPumpStatusData();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void initPumpStatusData();
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
Context context = MainApp.instance().getApplicationContext();
|
||||||
|
Intent intent = new Intent(context, getServiceClass());
|
||||||
|
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||||
|
|
||||||
|
serviceRunning = true;
|
||||||
|
|
||||||
|
MainApp.bus().register(this);
|
||||||
|
onStartCustomActions();
|
||||||
|
super.onStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
Context context = MainApp.instance().getApplicationContext();
|
||||||
|
context.unbindService(serviceConnection);
|
||||||
|
|
||||||
|
serviceRunning = false;
|
||||||
|
|
||||||
|
MainApp.bus().unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we need to run any custom actions in onStart (triggering events, etc)
|
||||||
|
*/
|
||||||
|
public abstract void onStartCustomActions();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity activity) {
|
||||||
|
confirmPumpPluginActivation(pluginSwitcher, activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service class (same one you did serviceConnection for)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract Class getServiceClass();
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("UnusedParameters")
|
||||||
|
@Subscribe
|
||||||
|
public void onStatusEvent(final EventAppExit e) {
|
||||||
|
MainApp.instance().getApplicationContext().unbindService(serviceConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PumpStatus getPumpStatusData() {
|
||||||
|
return pumpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return PumpDriverState.isInitialized(pumpState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isSuspended() {
|
||||||
|
return pumpState == PumpDriverState.Suspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isBusy() {
|
||||||
|
return pumpState == PumpDriverState.Busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
if (displayConnectionMessages && isLoggingEnabled())
|
||||||
|
LOG.warn("isConnected [PumpPluginAbstract].");
|
||||||
|
return PumpDriverState.isConnected(pumpState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isConnecting() {
|
||||||
|
if (displayConnectionMessages && isLoggingEnabled())
|
||||||
|
LOG.warn("isConnecting [PumpPluginAbstract].");
|
||||||
|
return pumpState == PumpDriverState.Connecting;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void connect(String reason) {
|
||||||
|
if (displayConnectionMessages && isLoggingEnabled())
|
||||||
|
LOG.warn("connect (reason={}) [PumpPluginAbstract] - default (empty) implementation.", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void disconnect(String reason) {
|
||||||
|
if (displayConnectionMessages && isLoggingEnabled())
|
||||||
|
LOG.warn("disconnect (reason={}) [PumpPluginAbstract] - default (empty) implementation.", reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void stopConnecting() {
|
||||||
|
if (displayConnectionMessages && isLoggingEnabled())
|
||||||
|
LOG.warn("stopConnecting [PumpPluginAbstract] - default (empty) implementation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHandshakeInProgress() {
|
||||||
|
if (displayConnectionMessages && isLoggingEnabled())
|
||||||
|
LOG.warn("isHandshakeInProgress [PumpPluginAbstract] - default (empty) implementation.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void finishHandshaking() {
|
||||||
|
if (displayConnectionMessages && isLoggingEnabled())
|
||||||
|
LOG.warn("finishHandshaking [PumpPluginAbstract] - default (empty) implementation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void getPumpStatus() {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("getPumpStatus [PumpPluginAbstract] - Not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Upload to pump new basal profile
|
||||||
|
public PumpEnactResult setNewBasalProfile(Profile profile) {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("setNewBasalProfile [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isThisProfileSet(Profile profile) {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("isThisProfileSet [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public long lastDataTime() {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("lastDataTime [PumpPluginAbstract].");
|
||||||
|
return pumpStatus.lastConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double getBaseBasalRate() {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("getBaseBasalRate [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return 0.0d;
|
||||||
|
} // base basal rate, not temp basal
|
||||||
|
|
||||||
|
|
||||||
|
public void stopBolusDelivering() {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("stopBolusDelivering [PumpPluginAbstract] - Not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult setTempBasalAbsolute(Double absoluteRate, Integer durationInMinutes, Profile profile,
|
||||||
|
boolean enforceNew) {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("setTempBasalAbsolute [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult setTempBasalPercent(Integer percent, Integer durationInMinutes, Profile profile,
|
||||||
|
boolean enforceNew) {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("setTempBasalPercent [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PumpEnactResult setExtendedBolus(Double insulin, Integer durationInMinutes) {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("setExtendedBolus [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// some pumps might set a very short temp close to 100% as cancelling a temp can be noisy
|
||||||
|
// when the cancel request is requested by the user (forced), the pump should always do a real cancel
|
||||||
|
|
||||||
|
public PumpEnactResult cancelTempBasal(boolean enforceNew) {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("cancelTempBasal [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PumpEnactResult cancelExtendedBolus() {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("cancelExtendedBolus [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Status to be passed to NS
|
||||||
|
|
||||||
|
// public JSONObject getJSONStatus(Profile profile, String profileName) {
|
||||||
|
// return pumpDriver.getJSONStatus(profile, profileName);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public String deviceID() {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("deviceID [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return "FakeDevice";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Pump capabilities
|
||||||
|
|
||||||
|
public PumpDescription getPumpDescription() {
|
||||||
|
return pumpDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Short info for SMS, Wear etc
|
||||||
|
|
||||||
|
public boolean isFakingTempsByExtendedBoluses() {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("isFakingTempsByExtendedBoluses [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult loadTDDs() {
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.warn("loadTDDs [PumpPluginAbstract] - Not implemented.");
|
||||||
|
return getOperationNotSupportedWithCustomText(R.string.pump_operation_not_supported_by_pump_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getJSONStatus(Profile profile, String profileName) {
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if ((pumpStatus.lastConnection + 5 * 60 * 1000L) < System.currentTimeMillis()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject pump = new JSONObject();
|
||||||
|
JSONObject battery = new JSONObject();
|
||||||
|
JSONObject status = new JSONObject();
|
||||||
|
JSONObject extended = new JSONObject();
|
||||||
|
try {
|
||||||
|
battery.put("percent", pumpStatus.batteryRemaining);
|
||||||
|
status.put("status", pumpStatus.pumpStatusType != null ? pumpStatus.pumpStatusType.getStatus() : "normal");
|
||||||
|
extended.put("Version", BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION);
|
||||||
|
try {
|
||||||
|
extended.put("ActiveProfile", profileName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
TemporaryBasal tb = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis());
|
||||||
|
if (tb != null) {
|
||||||
|
extended.put("TempBasalAbsoluteRate",
|
||||||
|
tb.tempBasalConvertedToAbsolute(System.currentTimeMillis(), profile));
|
||||||
|
extended.put("TempBasalStart", DateUtil.dateAndTimeString(tb.date));
|
||||||
|
extended.put("TempBasalRemaining", tb.getPlannedRemainingMinutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
ExtendedBolus eb = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(System.currentTimeMillis());
|
||||||
|
if (eb != null) {
|
||||||
|
extended.put("ExtendedBolusAbsoluteRate", eb.absoluteRate());
|
||||||
|
extended.put("ExtendedBolusStart", DateUtil.dateAndTimeString(eb.date));
|
||||||
|
extended.put("ExtendedBolusRemaining", eb.getPlannedRemainingMinutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
status.put("timestamp", DateUtil.toISOString(new Date()));
|
||||||
|
|
||||||
|
pump.put("battery", battery);
|
||||||
|
pump.put("status", status);
|
||||||
|
pump.put("extended", extended);
|
||||||
|
pump.put("reservoir", pumpStatus.reservoirRemainingUnits);
|
||||||
|
pump.put("clock", DateUtil.toISOString(new Date()));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
LOG.error("Unhandled exception", e);
|
||||||
|
}
|
||||||
|
return pump;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME i18n, null checks: iob, TDD
|
||||||
|
@Override
|
||||||
|
public String shortStatus(boolean veryShort) {
|
||||||
|
String ret = "";
|
||||||
|
if (pumpStatus.lastConnection != 0) {
|
||||||
|
Long agoMsec = System.currentTimeMillis() - pumpStatus.lastConnection;
|
||||||
|
int agoMin = (int)(agoMsec / 60d / 1000d);
|
||||||
|
ret += "LastConn: " + agoMin + " min ago\n";
|
||||||
|
}
|
||||||
|
if (pumpStatus.lastBolusTime != null && pumpStatus.lastBolusTime.getTime() != 0) {
|
||||||
|
ret += "LastBolus: " + DecimalFormatter.to2Decimal(pumpStatus.lastBolusAmount) + "U @" + //
|
||||||
|
android.text.format.DateFormat.format("HH:mm", pumpStatus.lastBolusTime) + "\n";
|
||||||
|
}
|
||||||
|
TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin()
|
||||||
|
.getRealTempBasalFromHistory(System.currentTimeMillis());
|
||||||
|
if (activeTemp != null) {
|
||||||
|
ret += "Temp: " + activeTemp.toStringFull() + "\n";
|
||||||
|
}
|
||||||
|
ExtendedBolus activeExtendedBolus = TreatmentsPlugin.getPlugin().getExtendedBolusFromHistory(
|
||||||
|
System.currentTimeMillis());
|
||||||
|
if (activeExtendedBolus != null) {
|
||||||
|
ret += "Extended: " + activeExtendedBolus.toString() + "\n";
|
||||||
|
}
|
||||||
|
// if (!veryShort) {
|
||||||
|
// ret += "TDD: " + DecimalFormatter.to0Decimal(pumpStatus.dailyTotalUnits) + " / "
|
||||||
|
// + pumpStatus.maxDailyTotalUnits + " U\n";
|
||||||
|
// }
|
||||||
|
ret += "IOB: " + pumpStatus.iob + "U\n";
|
||||||
|
ret += "Reserv: " + DecimalFormatter.to0Decimal(pumpStatus.reservoirRemainingUnits) + "U\n";
|
||||||
|
ret += "Batt: " + pumpStatus.batteryRemaining + "\n";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PumpEnactResult deliverTreatment(DetailedBolusInfo detailedBolusInfo) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (detailedBolusInfo.insulin == 0 && detailedBolusInfo.carbs == 0) {
|
||||||
|
// neither carbs nor bolus requested
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.error("deliverTreatment: Invalid input");
|
||||||
|
return new PumpEnactResult().success(false).enacted(false).bolusDelivered(0d).carbsDelivered(0d)
|
||||||
|
.comment(MainApp.gs(R.string.danar_invalidinput));
|
||||||
|
} else if (detailedBolusInfo.insulin > 0) {
|
||||||
|
// bolus needed, ask pump to deliver it
|
||||||
|
return deliverBolus(detailedBolusInfo);
|
||||||
|
} else {
|
||||||
|
// no bolus required, carb only treatment
|
||||||
|
TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true);
|
||||||
|
|
||||||
|
EventOverviewBolusProgress bolusingEvent = EventOverviewBolusProgress.getInstance();
|
||||||
|
bolusingEvent.t = new Treatment();
|
||||||
|
bolusingEvent.t.isSMB = detailedBolusInfo.isSMB;
|
||||||
|
bolusingEvent.percent = 100;
|
||||||
|
MainApp.bus().post(bolusingEvent);
|
||||||
|
|
||||||
|
if (isLoggingEnabled())
|
||||||
|
LOG.debug("deliverTreatment: Carb only treatment.");
|
||||||
|
|
||||||
|
return new PumpEnactResult().success(true).enacted(true).bolusDelivered(0d)
|
||||||
|
.carbsDelivered(detailedBolusInfo.carbs).comment(MainApp.gs(R.string.virtualpump_resultok));
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
triggerUIChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isLoggingEnabled() {
|
||||||
|
return L.isEnabled(L.PUMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract PumpEnactResult deliverBolus(DetailedBolusInfo detailedBolusInfo);
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract void triggerUIChange();
|
||||||
|
|
||||||
|
|
||||||
|
public static PumpEnactResult getOperationNotSupportedWithCustomText(int resourceId) {
|
||||||
|
return new PumpEnactResult().success(false).enacted(false).comment(MainApp.gs(resourceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.data;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.joda.time.LocalDateTime;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.data.ProfileStore;
|
||||||
|
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpStatusType;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 4/28/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public abstract class PumpStatus {
|
||||||
|
|
||||||
|
// connection
|
||||||
|
public LocalDateTime lastDataTime;
|
||||||
|
public long lastConnection = 0L;
|
||||||
|
public long previousConnection = 0L; // here should be stored last connection of previous session (so needs to be
|
||||||
|
// read before lastConnection is modified for first time).
|
||||||
|
|
||||||
|
// last bolus
|
||||||
|
public Date lastBolusTime;
|
||||||
|
public Double lastBolusAmount;
|
||||||
|
|
||||||
|
// other pump settings
|
||||||
|
public String activeProfileName = "0";
|
||||||
|
public double reservoirRemainingUnits = 0d;
|
||||||
|
public String reservoirFullUnits = "???";
|
||||||
|
public int batteryRemaining = 0; // percent, so 0-100
|
||||||
|
public Double batteryVoltage = null;
|
||||||
|
|
||||||
|
|
||||||
|
// iob
|
||||||
|
public String iob = null;
|
||||||
|
|
||||||
|
// TDD
|
||||||
|
public Double dailyTotalUnits;
|
||||||
|
public String maxDailyTotalUnits;
|
||||||
|
public boolean validBasalRateProfileSelectedOnPump = true;
|
||||||
|
public PumpType pumpType = PumpType.GenericAAPS;
|
||||||
|
public ProfileStore profileStore;
|
||||||
|
public String units; // Constants.MGDL or Constants.MMOL
|
||||||
|
public PumpStatusType pumpStatusType = PumpStatusType.Running;
|
||||||
|
public Double[] basalsByHour;
|
||||||
|
public double currentBasal = 0;
|
||||||
|
public int tempBasalInProgress = 0;
|
||||||
|
public int tempBasalRatio = 0;
|
||||||
|
public int tempBasalRemainMin = 0;
|
||||||
|
public Date tempBasalStart;
|
||||||
|
protected PumpDescription pumpDescription;
|
||||||
|
|
||||||
|
|
||||||
|
public PumpStatus(PumpDescription pumpDescription) {
|
||||||
|
this.pumpDescription = pumpDescription;
|
||||||
|
|
||||||
|
this.initSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void initSettings();
|
||||||
|
|
||||||
|
|
||||||
|
public void setLastCommunicationToNow() {
|
||||||
|
this.lastDataTime = LocalDateTime.now();
|
||||||
|
this.lastConnection = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public abstract String getErrorInfo();
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void refreshConfiguration();
|
||||||
|
|
||||||
|
|
||||||
|
public PumpType getPumpType() {
|
||||||
|
return pumpType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setPumpType(PumpType pumpType) {
|
||||||
|
this.pumpType = pumpType;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public Date last_bolus_time;
|
||||||
|
// public double last_bolus_amount = 0;
|
||||||
|
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ public enum PumpCapability {
|
||||||
DanaCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, TDD, ManualTDDLoad), //
|
DanaCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, TDD, ManualTDDLoad), //
|
||||||
DanaWithHistoryCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, StoreCarbInfo, TDD, ManualTDDLoad), //
|
DanaWithHistoryCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, StoreCarbInfo, TDD, ManualTDDLoad), //
|
||||||
InsightCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill,TDD,BasalRate30min), //
|
InsightCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill,TDD,BasalRate30min), //
|
||||||
|
MedtronicCapabilities(Bolus, TempBasal, BasalProfileSet, Refill, TDD), //
|
||||||
|
|
||||||
// BasalRates (separately grouped)
|
// BasalRates (separately grouped)
|
||||||
BasalRate_Duration15minAllowed, //
|
BasalRate_Duration15minAllowed, //
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.defs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 10/15/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum PumpDriverState {
|
||||||
|
|
||||||
|
NotInitialized, //
|
||||||
|
Connecting, //
|
||||||
|
Connected, //
|
||||||
|
Initialized, //
|
||||||
|
Ready,
|
||||||
|
Busy, //
|
||||||
|
Suspended, //
|
||||||
|
;
|
||||||
|
|
||||||
|
public static boolean isConnected(PumpDriverState pumpState) {
|
||||||
|
return pumpState == Connected || pumpState == Initialized || pumpState == Busy || pumpState == Suspended;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isInitialized(PumpDriverState pumpState) {
|
||||||
|
return pumpState == Initialized || pumpState == Busy || pumpState == Suspended;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.defs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/12/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum PumpStatusType {
|
||||||
|
Running("normal"), //
|
||||||
|
Suspended("suspended") //
|
||||||
|
;
|
||||||
|
|
||||||
|
private String statusString;
|
||||||
|
|
||||||
|
|
||||||
|
PumpStatusType(String statusString) {
|
||||||
|
this.statusString = statusString;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return statusString;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import java.util.Map;
|
||||||
|
|
||||||
import info.nightscout.androidaps.MainApp;
|
import info.nightscout.androidaps.MainApp;
|
||||||
import info.nightscout.androidaps.R;
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.common.ManufacturerType;
|
||||||
import info.nightscout.androidaps.plugins.pump.common.data.DoseSettings;
|
import info.nightscout.androidaps.plugins.pump.common.data.DoseSettings;
|
||||||
import info.nightscout.androidaps.utils.Round;
|
import info.nightscout.androidaps.utils.Round;
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ import info.nightscout.androidaps.utils.Round;
|
||||||
|
|
||||||
public enum PumpType {
|
public enum PumpType {
|
||||||
|
|
||||||
GenericAAPS("Generic AAPS", "AndroidAPS", "VirutalPump", 0.1d, null, //
|
GenericAAPS("Generic AAPS", ManufacturerType.AndroidAPS, "VirutalPump", 0.1d, null, //
|
||||||
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10, 30, 24 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
new DoseSettings(10, 30, 24 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||||
|
@ -26,7 +27,7 @@ public enum PumpType {
|
||||||
|
|
||||||
// Cellnovo
|
// Cellnovo
|
||||||
|
|
||||||
Cellnovo1("Cellnovo", "Cellnovo", "Cellnovo", 0.05d, null, //
|
Cellnovo1("Cellnovo", ManufacturerType.Cellnovo, "Cellnovo", 0.05d, null, //
|
||||||
new DoseSettings(0.05d, 30, 24 * 60, 1d, null),
|
new DoseSettings(0.05d, 30, 24 * 60, 1d, null),
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(5, 30, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration30minAllowed, //
|
new DoseSettings(5, 30, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||||
|
@ -34,32 +35,32 @@ public enum PumpType {
|
||||||
|
|
||||||
// Accu-Chek
|
// Accu-Chek
|
||||||
|
|
||||||
AccuChekCombo("Accu-Chek Combo", "Roche", "Combo", 0.1d, null, //
|
AccuChekCombo("Accu-Chek Combo", ManufacturerType.Roche, "Combo", 0.1d, null, //
|
||||||
new DoseSettings(0.1d, 15, 12 * 60, 0.1d), //
|
new DoseSettings(0.1d, 15, 12 * 60, 0.1d), //
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(10, 15, 12 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
new DoseSettings(10, 15, 12 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||||
0.01d, 0.01d, DoseStepSize.ComboBasal, PumpCapability.ComboCapabilities), //
|
0.01d, 0.01d, DoseStepSize.ComboBasal, PumpCapability.ComboCapabilities), //
|
||||||
|
|
||||||
AccuChekSpirit("Accu-Chek Spirit", "Roche", "Spirit", 0.1d, null, //
|
AccuChekSpirit("Accu-Chek Spirit", ManufacturerType.Roche, "Spirit", 0.1d, null, //
|
||||||
new DoseSettings(0.1d, 15, 12 * 60, 0.1d), //
|
new DoseSettings(0.1d, 15, 12 * 60, 0.1d), //
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(10, 15, 12 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
new DoseSettings(10, 15, 12 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||||
0.01d, 0.1d, null, PumpCapability.VirtualPumpCapabilities), //
|
0.01d, 0.1d, null, PumpCapability.VirtualPumpCapabilities), //
|
||||||
|
|
||||||
AccuChekInsight("Accu-Chek Insight", "Roche", "Insight", 0.05d, DoseStepSize.InsightBolus, //
|
AccuChekInsight("Accu-Chek Insight", ManufacturerType.Roche, "Insight", 0.05d, DoseStepSize.InsightBolus, //
|
||||||
new DoseSettings(0.05d, 15, 24 * 60, 0.05d), //
|
new DoseSettings(0.05d, 15, 24 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(10, 15, 24 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
new DoseSettings(10, 15, 24 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||||
0.02d, 0.01d, null, PumpCapability.InsightCapabilities), //
|
0.02d, 0.01d, null, PumpCapability.InsightCapabilities), //
|
||||||
|
|
||||||
AccuChekInsightBluetooth("Accu-Chek Insight", "Roche", "Insight", 0.01d, null, //
|
AccuChekInsightBluetooth("Accu-Chek Insight", ManufacturerType.Roche, "Insight", 0.01d, null, //
|
||||||
new DoseSettings(0.01d, 15, 24 * 60, 0.05d), //
|
new DoseSettings(0.01d, 15, 24 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(10, 15, 24 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
new DoseSettings(10, 15, 24 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||||
0.02d, 0.01d, DoseStepSize.InsightBolus, PumpCapability.InsightCapabilities), //
|
0.02d, 0.01d, DoseStepSize.InsightBolus, PumpCapability.InsightCapabilities), //
|
||||||
|
|
||||||
// Animas
|
// Animas
|
||||||
AnimasVibe("Animas Vibe","Animas", "Vibe", 0.05d, null, // AnimasBolus?
|
AnimasVibe("Animas Vibe",ManufacturerType.Animas, "Vibe", 0.05d, null, // AnimasBolus?
|
||||||
new DoseSettings(0.05d, 30, 12 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 12 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10, 30, 24 * 60, 0d, 300d), PumpCapability.BasalRate_Duration30minAllowed, //
|
new DoseSettings(10, 30, 24 * 60, 0d, 300d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||||
|
@ -68,19 +69,19 @@ public enum PumpType {
|
||||||
AnimasPing("Animas Ping", "Ping", AnimasVibe),
|
AnimasPing("Animas Ping", "Ping", AnimasVibe),
|
||||||
|
|
||||||
// Dana
|
// Dana
|
||||||
DanaR("DanaR", "SOOIL", "DanaR", 0.05d, null, //
|
DanaR("DanaR", ManufacturerType.Sooil, "DanaR", 0.05d, null, //
|
||||||
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, //
|
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, //
|
||||||
0.04d, 0.01d, null, PumpCapability.DanaCapabilities),
|
0.04d, 0.01d, null, PumpCapability.DanaCapabilities),
|
||||||
|
|
||||||
DanaRKorean("DanaR Korean", "SOOIL", "DanaRKorean", 0.05d, null, //
|
DanaRKorean("DanaR Korean", ManufacturerType.Sooil, "DanaRKorean", 0.05d, null, //
|
||||||
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, //
|
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, //
|
||||||
0.1d, 0.01d, null, PumpCapability.DanaCapabilities),
|
0.1d, 0.01d, null, PumpCapability.DanaCapabilities),
|
||||||
|
|
||||||
DanaRS("DanaRS", "SOOIL", "DanaRS", 0.05d, null, //
|
DanaRS("DanaRS", ManufacturerType.Sooil, "DanaRS", 0.05d, null, //
|
||||||
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Percent, //
|
PumpTempBasalType.Percent, //
|
||||||
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||||
|
@ -90,38 +91,38 @@ public enum PumpType {
|
||||||
|
|
||||||
|
|
||||||
// Insulet
|
// Insulet
|
||||||
Insulet_Omnipod("Insulet Omnipod", "Insulet", "Omnipod", 0.05d, null, //
|
Insulet_Omnipod("Insulet Omnipod", ManufacturerType.Insulet, "Omnipod", 0.05d, null, //
|
||||||
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Absolute, //
|
PumpTempBasalType.Absolute, //
|
||||||
new DoseSettings(0.05d, 30, 12 * 60, 0d, 30.0d), PumpCapability.BasalRate_Duration30minAllowed, // cannot exceed max basal rate 30u/hr
|
new DoseSettings(0.05d, 30, 12 * 60, 0d, 30.0d), PumpCapability.BasalRate_Duration30minAllowed, // cannot exceed max basal rate 30u/hr
|
||||||
0.05d, 0.05d, null, PumpCapability.VirtualPumpCapabilities),
|
0.05d, 0.05d, null, PumpCapability.VirtualPumpCapabilities),
|
||||||
|
|
||||||
// Medtronic
|
// Medtronic
|
||||||
Medtronic_512_712("Medtronic 512/712", "Medtronic", "512/712", 0.05d, null, //
|
Medtronic_512_712("Medtronic 512/712", ManufacturerType.Medtronic, "512/712", 0.05d, null, //
|
||||||
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Absolute, //
|
PumpTempBasalType.Absolute, //
|
||||||
new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||||
0.05d, 0.05d, null, PumpCapability.VirtualPumpCapabilities), // TODO
|
0.05d, 0.05d, null, PumpCapability.MedtronicCapabilities), //
|
||||||
|
|
||||||
Medtronic_515_715("Medtronic 515/715", "515/715", Medtronic_512_712),
|
Medtronic_515_715("Medtronic 515/715", "515/715", Medtronic_512_712),
|
||||||
Medtronic_522_722("Medtronic 522/722", "522/722", Medtronic_512_712),
|
Medtronic_522_722("Medtronic 522/722", "522/722", Medtronic_512_712),
|
||||||
|
|
||||||
Medtronic_523_723_Revel("Medtronic 523/723 (Revel)", "Medtronic", "523/723 (Revel)", 0.05d, null, //
|
Medtronic_523_723_Revel("Medtronic 523/723 (Revel)", ManufacturerType.Medtronic, "523/723 (Revel)", 0.05d, null, //
|
||||||
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Absolute, //
|
PumpTempBasalType.Absolute, //
|
||||||
new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||||
0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPumpCapabilities), //
|
0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.MedtronicCapabilities), //
|
||||||
|
|
||||||
Medtronic_554_754_Veo("Medtronic 554/754 (Veo)", "554/754 (Veo)", Medtronic_523_723_Revel), // TODO
|
Medtronic_554_754_Veo("Medtronic 554/754 (Veo)", "554/754 (Veo)", Medtronic_523_723_Revel), // TODO
|
||||||
|
|
||||||
Medtronic_640G("Medtronic 640G", "Medtronic", "640G", 0.025d, null, //
|
Medtronic_640G("Medtronic 640G", ManufacturerType.Medtronic, "640G", 0.025d, null, //
|
||||||
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
new DoseSettings(0.05d, 30, 8 * 60, 0.05d), //
|
||||||
PumpTempBasalType.Absolute, //
|
PumpTempBasalType.Absolute, //
|
||||||
new DoseSettings(0.05d, 30, 24 * 60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
new DoseSettings(0.05d, 30, 24 * 60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||||
0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPumpCapabilities), //
|
0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPumpCapabilities), //
|
||||||
|
|
||||||
// Tandem
|
// Tandem
|
||||||
TandemTSlim("Tandem t:slim", "Tandem", "t:slim", 0.01d, null, //
|
TandemTSlim("Tandem t:slim", ManufacturerType.Tandem, "t:slim", 0.01d, null, //
|
||||||
new DoseSettings(0.01d, 15, 8 * 60, 0.4d),
|
new DoseSettings(0.01d, 15, 8 * 60, 0.4d),
|
||||||
PumpTempBasalType.Percent,
|
PumpTempBasalType.Percent,
|
||||||
new DoseSettings(1, 15, 8 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
new DoseSettings(1, 15, 8 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||||
|
@ -130,10 +131,14 @@ public enum PumpType {
|
||||||
TandemTFlex("Tandem t:flex", "t:flex", TandemTSlim), //
|
TandemTFlex("Tandem t:flex", "t:flex", TandemTSlim), //
|
||||||
TandemTSlimG4("Tandem t:slim G4", "t:slim G4", TandemTSlim), //
|
TandemTSlimG4("Tandem t:slim G4", "t:slim G4", TandemTSlim), //
|
||||||
TandemTSlimX2("Tandem t:slim X2", "t:slim X2", TandemTSlim), //
|
TandemTSlimX2("Tandem t:slim X2", "t:slim X2", TandemTSlim), //
|
||||||
|
|
||||||
|
// MDI
|
||||||
|
MDI("MDI", ManufacturerType.AndroidAPS, "MDI")
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
private String description;
|
private String description;
|
||||||
private String manufacter;
|
private ManufacturerType manufacturer;
|
||||||
private String model;
|
private String model;
|
||||||
private double bolusSize;
|
private double bolusSize;
|
||||||
private DoseStepSize specialBolusSize;
|
private DoseStepSize specialBolusSize;
|
||||||
|
@ -163,9 +168,18 @@ public enum PumpType {
|
||||||
{
|
{
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
parent.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PumpType(String description, ManufacturerType manufacturer, String model)
|
||||||
|
{
|
||||||
|
this.description = description;
|
||||||
|
this.manufacturer = manufacturer;
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PumpType(String description, String model, PumpType parent, PumpCapability pumpCapability)
|
PumpType(String description, String model, PumpType parent, PumpCapability pumpCapability)
|
||||||
{
|
{
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
@ -174,20 +188,20 @@ public enum PumpType {
|
||||||
parent.model = model;
|
parent.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
PumpType(String description, String manufacter, String model, double bolusSize, DoseStepSize specialBolusSize, //
|
PumpType(String description, ManufacturerType manufacturer, String model, double bolusSize, DoseStepSize specialBolusSize, //
|
||||||
DoseSettings extendedBolusSettings, //
|
DoseSettings extendedBolusSettings, //
|
||||||
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, //
|
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, //
|
||||||
double baseBasalMinValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability)
|
double baseBasalMinValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability)
|
||||||
{
|
{
|
||||||
this(description, manufacter, model, bolusSize, specialBolusSize, extendedBolusSettings, pumpTempBasalType, tbrSettings, specialBasalDurations, baseBasalMinValue, null, baseBasalStep, baseBasalSpecialSteps, pumpCapability);
|
this(description, manufacturer, model, bolusSize, specialBolusSize, extendedBolusSettings, pumpTempBasalType, tbrSettings, specialBasalDurations, baseBasalMinValue, null, baseBasalStep, baseBasalSpecialSteps, pumpCapability);
|
||||||
}
|
}
|
||||||
|
|
||||||
PumpType(String description, String manufacter, String model, double bolusSize, DoseStepSize specialBolusSize, //
|
PumpType(String description, ManufacturerType manufacturer, String model, double bolusSize, DoseStepSize specialBolusSize, //
|
||||||
DoseSettings extendedBolusSettings, //
|
DoseSettings extendedBolusSettings, //
|
||||||
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, //
|
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, //
|
||||||
double baseBasalMinValue, Double baseBasalMaxValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) {
|
double baseBasalMinValue, Double baseBasalMaxValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.manufacter = manufacter;
|
this.manufacturer = manufacturer;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.bolusSize = bolusSize;
|
this.bolusSize = bolusSize;
|
||||||
this.specialBolusSize = specialBolusSize;
|
this.specialBolusSize = specialBolusSize;
|
||||||
|
@ -207,8 +221,8 @@ public enum PumpType {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getManufacter() {
|
public ManufacturerType getManufacturer() {
|
||||||
return isParentSet() ? parent.manufacter : manufacter;
|
return isParentSet() ? parent.manufacturer : manufacturer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getModel() {
|
public String getModel() {
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.dialog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 5/19/18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface RefreshableInterface {
|
||||||
|
|
||||||
|
void refreshData();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,423 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.dialog;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.le.BluetoothLeScanner;
|
||||||
|
import android.bluetooth.le.ScanCallback;
|
||||||
|
import android.bluetooth.le.ScanFilter;
|
||||||
|
import android.bluetooth.le.ScanResult;
|
||||||
|
import android.bluetooth.le.ScanSettings;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.LocationHelper;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.driver.MedtronicPumpStatus;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicPumpConfigurationChanged;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil;
|
||||||
|
import info.nightscout.androidaps.utils.SP;
|
||||||
|
|
||||||
|
// IMPORTANT: This activity needs to be called from RileyLinkSelectPreference (see pref_medtronic.xml as example)
|
||||||
|
public class RileyLinkBLEScanActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RileyLinkBLEScanActivity.class);
|
||||||
|
|
||||||
|
private static final int PERMISSION_REQUEST_COARSE_LOCATION = 30241; // arbitrary.
|
||||||
|
private static final int REQUEST_ENABLE_BT = 30242; // arbitrary
|
||||||
|
|
||||||
|
private static String TAG = "RileyLinkBLEScanActivity";
|
||||||
|
|
||||||
|
// Stops scanning after 30 seconds.
|
||||||
|
private static final long SCAN_PERIOD = 30000;
|
||||||
|
public boolean mScanning;
|
||||||
|
public ScanSettings settings;
|
||||||
|
public List<ScanFilter> filters;
|
||||||
|
public ListView listBTScan;
|
||||||
|
public Toolbar toolbarBTScan;
|
||||||
|
public Context mContext = this;
|
||||||
|
private BluetoothAdapter mBluetoothAdapter;
|
||||||
|
private BluetoothLeScanner mLEScanner;
|
||||||
|
private LeDeviceListAdapter mLeDeviceListAdapter;
|
||||||
|
private Handler mHandler;
|
||||||
|
|
||||||
|
private String actionTitleStart, actionTitleStop;
|
||||||
|
private MenuItem menuItem;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.rileylink_scan_activity);
|
||||||
|
|
||||||
|
// Initializes Bluetooth adapter.
|
||||||
|
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
mHandler = new Handler();
|
||||||
|
|
||||||
|
mLeDeviceListAdapter = new LeDeviceListAdapter();
|
||||||
|
listBTScan = (ListView)findViewById(R.id.rileylink_listBTScan);
|
||||||
|
listBTScan.setAdapter(mLeDeviceListAdapter);
|
||||||
|
listBTScan.setOnItemClickListener((parent, view, position, id) -> {
|
||||||
|
|
||||||
|
// stop scanning if still active
|
||||||
|
if (mScanning) {
|
||||||
|
mScanning = false;
|
||||||
|
mLEScanner.stopScan(mScanCallback2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextView textview = (TextView)view.findViewById(R.id.rileylink_device_address);
|
||||||
|
String bleAddress = textview.getText().toString();
|
||||||
|
|
||||||
|
SP.putString(RileyLinkConst.Prefs.RileyLinkAddress, bleAddress);
|
||||||
|
|
||||||
|
RileyLinkUtil.getRileyLinkSelectPreference().setSummary(bleAddress);
|
||||||
|
|
||||||
|
MedtronicPumpStatus pumpStatus = MedtronicUtil.getPumpStatus();
|
||||||
|
pumpStatus.verifyConfiguration(); // force reloading of address
|
||||||
|
|
||||||
|
MainApp.bus().post(new EventMedtronicPumpConfigurationChanged());
|
||||||
|
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
toolbarBTScan = (Toolbar)findViewById(R.id.rileylink_toolbarBTScan);
|
||||||
|
toolbarBTScan.setTitle(R.string.rileylink_scanner_title);
|
||||||
|
setSupportActionBar(toolbarBTScan);
|
||||||
|
|
||||||
|
prepareForScanning();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_rileylink_ble_scan, menu);
|
||||||
|
|
||||||
|
actionTitleStart = MainApp.gs(R.string.rileylink_scanner_scan_scan);
|
||||||
|
actionTitleStop = MainApp.gs(R.string.rileylink_scanner_scan_stop);
|
||||||
|
|
||||||
|
menuItem = menu.getItem(0);
|
||||||
|
|
||||||
|
menuItem.setTitle(actionTitleStart);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.rileylink_miScan: {
|
||||||
|
scanLeDevice(menuItem.getTitle().equals(actionTitleStart));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void prepareForScanning() {
|
||||||
|
// https://developer.android.com/training/permissions/requesting.html
|
||||||
|
// http://developer.radiusnetworks.com/2015/09/29/is-your-beacon-app-ready-for-android-6.html
|
||||||
|
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||||
|
Toast.makeText(this, R.string.rileylink_scanner_ble_not_supported, Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
// Use this check to determine whether BLE is supported on the device. Then
|
||||||
|
// you can selectively disable BLE-related features.
|
||||||
|
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// your code that requires permission
|
||||||
|
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.ACCESS_COARSE_LOCATION },
|
||||||
|
PERMISSION_REQUEST_COARSE_LOCATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensures Bluetooth is available on the device and it is enabled. If not,
|
||||||
|
// displays a dialog requesting user permission to enable Bluetooth.
|
||||||
|
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
|
||||||
|
Toast.makeText(this, R.string.rileylink_scanner_ble_not_enabled, Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
// Will request that GPS be enabled for devices running Marshmallow or newer.
|
||||||
|
if (!LocationHelper.isLocationEnabled(this)) {
|
||||||
|
LocationHelper.requestLocationForBluetooth(this);
|
||||||
|
}
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
|
||||||
|
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
|
||||||
|
}
|
||||||
|
|
||||||
|
mLEScanner = mBluetoothAdapter.getBluetoothLeScanner();
|
||||||
|
settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
|
||||||
|
filters = Arrays.asList(new ScanFilter.Builder().setServiceUuid(
|
||||||
|
ParcelUuid.fromString(GattAttributes.SERVICE_RADIO)).build());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable currently selected RL, so that we can discover it
|
||||||
|
RileyLinkUtil.sendBroadcastMessage(RileyLinkConst.Intents.RileyLinkDisconnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
if (requestCode == REQUEST_ENABLE_BT) {
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
// User allowed Bluetooth to turn on
|
||||||
|
} else if (resultCode == RESULT_CANCELED) {
|
||||||
|
// Error, or user said "NO"
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ScanCallback mScanCallback2 = new ScanCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanResult(int callbackType, final ScanResult scanRecord) {
|
||||||
|
|
||||||
|
Log.d(TAG, scanRecord.toString());
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
if (addDevice(scanRecord))
|
||||||
|
mLeDeviceListAdapter.notifyDataSetChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBatchScanResults(final List<ScanResult> results) {
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
|
||||||
|
boolean added = false;
|
||||||
|
|
||||||
|
for (ScanResult result : results) {
|
||||||
|
if (addDevice(result))
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (added)
|
||||||
|
mLeDeviceListAdapter.notifyDataSetChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean addDevice(ScanResult result) {
|
||||||
|
|
||||||
|
BluetoothDevice device = result.getDevice();
|
||||||
|
|
||||||
|
List<ParcelUuid> serviceUuids = result.getScanRecord().getServiceUuids();
|
||||||
|
|
||||||
|
if (serviceUuids == null || serviceUuids.size() == 0) {
|
||||||
|
Log.v(TAG, "Device " + device.getAddress() + " has no serviceUuids (Not RileyLink).");
|
||||||
|
} else if (serviceUuids.size() > 1) {
|
||||||
|
Log.v(TAG, "Device " + device.getAddress() + " has too many serviceUuids (Not RileyLink).");
|
||||||
|
} else {
|
||||||
|
|
||||||
|
String uuid = serviceUuids.get(0).getUuid().toString().toLowerCase();
|
||||||
|
|
||||||
|
if (uuid.equals(GattAttributes.SERVICE_RADIO)) {
|
||||||
|
Log.i(TAG, "Found RileyLink with address: " + device.getAddress());
|
||||||
|
mLeDeviceListAdapter.addDevice(result);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "Device " + device.getAddress() + " has incorrect uuid (Not RileyLink).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getDeviceDebug(BluetoothDevice device) {
|
||||||
|
return "BluetoothDevice [name=" + device.getName() + ", address=" + device.getAddress() + //
|
||||||
|
", type=" + device.getType(); // + ", alias=" + device.getAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScanFailed(int errorCode) {
|
||||||
|
Log.e("Scan Failed", "Error Code: " + errorCode);
|
||||||
|
Toast.makeText(mContext, MainApp.gs(R.string.rileylink_scanner_scanning_error, errorCode),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
private void scanLeDevice(final boolean enable) {
|
||||||
|
|
||||||
|
if (mLEScanner == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
|
||||||
|
mLeDeviceListAdapter.clear();
|
||||||
|
mLeDeviceListAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
|
// Stops scanning after a pre-defined scan period.
|
||||||
|
mHandler.postDelayed(() -> {
|
||||||
|
|
||||||
|
if (mScanning) {
|
||||||
|
mScanning = false;
|
||||||
|
mLEScanner.stopScan(mScanCallback2);
|
||||||
|
LOG.debug("scanLeDevice: Scanning Stop");
|
||||||
|
Toast.makeText(mContext, R.string.rileylink_scanner_scanning_finished, Toast.LENGTH_SHORT).show();
|
||||||
|
menuItem.setTitle(actionTitleStart);
|
||||||
|
}
|
||||||
|
}, SCAN_PERIOD);
|
||||||
|
|
||||||
|
mScanning = true;
|
||||||
|
mLEScanner.startScan(filters, settings, mScanCallback2);
|
||||||
|
LOG.debug("scanLeDevice: Scanning Start");
|
||||||
|
Toast.makeText(this, R.string.rileylink_scanner_scanning, Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
menuItem.setTitle(actionTitleStop);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (mScanning) {
|
||||||
|
mScanning = false;
|
||||||
|
mLEScanner.stopScan(mScanCallback2);
|
||||||
|
|
||||||
|
LOG.debug("scanLeDevice: Scanning Stop");
|
||||||
|
Toast.makeText(this, R.string.rileylink_scanner_scanning_finished, Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
menuItem.setTitle(actionTitleStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LeDeviceListAdapter extends BaseAdapter {
|
||||||
|
|
||||||
|
private ArrayList<BluetoothDevice> mLeDevices;
|
||||||
|
private Map<BluetoothDevice, Integer> rileyLinkDevices;
|
||||||
|
private LayoutInflater mInflator;
|
||||||
|
String currentlySelectedAddress;
|
||||||
|
|
||||||
|
|
||||||
|
public LeDeviceListAdapter() {
|
||||||
|
super();
|
||||||
|
mLeDevices = new ArrayList<>();
|
||||||
|
rileyLinkDevices = new HashMap<>();
|
||||||
|
mInflator = RileyLinkBLEScanActivity.this.getLayoutInflater();
|
||||||
|
currentlySelectedAddress = SP.getString(RileyLinkConst.Prefs.RileyLinkAddress, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addDevice(ScanResult result) {
|
||||||
|
|
||||||
|
if (!mLeDevices.contains(result.getDevice())) {
|
||||||
|
mLeDevices.add(result.getDevice());
|
||||||
|
}
|
||||||
|
rileyLinkDevices.put(result.getDevice(), result.getRssi());
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
mLeDevices.clear();
|
||||||
|
rileyLinkDevices.clear();
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return mLeDevices.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int i) {
|
||||||
|
return mLeDevices.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int i) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||||
|
|
||||||
|
ViewHolder viewHolder;
|
||||||
|
// General ListView optimization code.
|
||||||
|
if (view == null) {
|
||||||
|
view = mInflator.inflate(R.layout.rileylink_scan_item, null);
|
||||||
|
viewHolder = new ViewHolder();
|
||||||
|
viewHolder.deviceAddress = (TextView)view.findViewById(R.id.rileylink_device_address);
|
||||||
|
viewHolder.deviceName = (TextView)view.findViewById(R.id.rileylink_device_name);
|
||||||
|
view.setTag(viewHolder);
|
||||||
|
} else {
|
||||||
|
viewHolder = (ViewHolder)view.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothDevice device = mLeDevices.get(i);
|
||||||
|
String deviceName = device.getName();
|
||||||
|
|
||||||
|
if (StringUtils.isBlank(deviceName)) {
|
||||||
|
deviceName = "RileyLink";
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceName += " [" + rileyLinkDevices.get(device).intValue() + "]";
|
||||||
|
|
||||||
|
if (currentlySelectedAddress.equals(device.getAddress())) {
|
||||||
|
// viewHolder.deviceName.setTextColor(getColor(R.color.secondary_text_light));
|
||||||
|
// viewHolder.deviceAddress.setTextColor(getColor(R.color.secondary_text_light));
|
||||||
|
deviceName += " (" + getResources().getString(R.string.rileylink_scanner_selected_device) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
viewHolder.deviceName.setText(deviceName);
|
||||||
|
viewHolder.deviceAddress.setText(device.getAddress());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ViewHolder {
|
||||||
|
|
||||||
|
TextView deviceName;
|
||||||
|
TextView deviceAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,439 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.hw.rileylink;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.data.PumpStatus;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.FrequencyScanResults;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.FrequencyTrial;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RFSpyResponse;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RLMessage;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioResponse;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RLMessageType;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkBLEError;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.WakeAndTuneTask;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.defs.PumpDeviceState;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicUtil;
|
||||||
|
import info.nightscout.androidaps.utils.SP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is abstract class for RileyLink Communication, this one needs to be extended by specific "Pump" class.
|
||||||
|
* <p>
|
||||||
|
* Created by andy on 5/10/18.
|
||||||
|
*/
|
||||||
|
public abstract class RileyLinkCommunicationManager {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM);
|
||||||
|
|
||||||
|
private static final int SCAN_TIMEOUT = 1500;
|
||||||
|
private static final int ALLOWED_PUMP_UNREACHABLE = 10 * 60 * 1000; // 10 minutes
|
||||||
|
|
||||||
|
protected final RFSpy rfspy;
|
||||||
|
protected final Context context;
|
||||||
|
protected int receiverDeviceAwakeForMinutes = 1; // override this in constructor of specific implementation
|
||||||
|
protected String receiverDeviceID; // String representation of receiver device (ex. Pump (xxxxxx) or Pod (yyyyyy))
|
||||||
|
protected long lastGoodReceiverCommunicationTime = 0;
|
||||||
|
protected PumpStatus pumpStatus;
|
||||||
|
protected RileyLinkServiceData rileyLinkServiceData;
|
||||||
|
private long nextWakeUpRequired = 0L;
|
||||||
|
|
||||||
|
// internal flag
|
||||||
|
private boolean showPumpMessages = true;
|
||||||
|
private int timeoutCount = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public RileyLinkCommunicationManager(Context context, RFSpy rfspy) {
|
||||||
|
this.context = context;
|
||||||
|
this.rfspy = rfspy;
|
||||||
|
this.rileyLinkServiceData = RileyLinkUtil.getRileyLinkServiceData();
|
||||||
|
RileyLinkUtil.setRileyLinkCommunicationManager(this);
|
||||||
|
|
||||||
|
configurePumpSpecificSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract void configurePumpSpecificSettings();
|
||||||
|
|
||||||
|
|
||||||
|
// All pump communications go through this function.
|
||||||
|
protected <E extends RLMessage> E sendAndListen(RLMessage msg, int timeout_ms, Class<E> clazz)
|
||||||
|
throws RileyLinkCommunicationException {
|
||||||
|
|
||||||
|
if (showPumpMessages) {
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.info("Sent:" + ByteUtil.shortHexString(msg.getTxData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
RFSpyResponse rfSpyResponse = rfspy.transmitThenReceive(new RadioPacket(msg.getTxData()), timeout_ms);
|
||||||
|
|
||||||
|
RadioResponse radioResponse = rfSpyResponse.getRadioResponse();
|
||||||
|
|
||||||
|
E response = createResponseMessage(rfSpyResponse.getRadioResponse().getPayload(), clazz);
|
||||||
|
|
||||||
|
if (response.isValid()) {
|
||||||
|
// Mark this as the last time we heard from the pump.
|
||||||
|
rememberLastGoodDeviceCommunicationTime();
|
||||||
|
} else {
|
||||||
|
LOG.warn("isDeviceReachable. Response is invalid ! [interrupted={}, timeout={}, unknownCommand={}, invalidParam={}]", rfSpyResponse.wasInterrupted(),
|
||||||
|
rfSpyResponse.wasTimeout(), rfSpyResponse.isUnknownCommand(), rfSpyResponse.isInvalidParam());
|
||||||
|
|
||||||
|
if (rfSpyResponse.wasTimeout()) {
|
||||||
|
timeoutCount++;
|
||||||
|
|
||||||
|
long diff = System.currentTimeMillis() - pumpStatus.lastConnection;
|
||||||
|
|
||||||
|
if (diff > ALLOWED_PUMP_UNREACHABLE) {
|
||||||
|
LOG.warn("We reached max time that Pump can be unreachable. Starting Tuning.");
|
||||||
|
ServiceTaskExecutor.startTask(new WakeAndTuneTask());
|
||||||
|
timeoutCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RileyLinkCommunicationException(RileyLinkBLEError.Timeout);
|
||||||
|
} else if (rfSpyResponse.wasInterrupted()) {
|
||||||
|
throw new RileyLinkCommunicationException(RileyLinkBLEError.Interrupted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showPumpMessages) {
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.info("Received:" + ByteUtil.shortHexString(rfSpyResponse.getRadioResponse().getPayload()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public abstract <E extends RLMessage> E createResponseMessage(byte[] payload, Class<E> clazz);
|
||||||
|
|
||||||
|
|
||||||
|
public void wakeUp(boolean force) {
|
||||||
|
wakeUp(receiverDeviceAwakeForMinutes, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getNotConnectedCount() {
|
||||||
|
return rfspy != null ? rfspy.notConnectedCount : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME change wakeup
|
||||||
|
// TODO we might need to fix this. Maybe make pump awake for shorter time (battery factor for pump) - Andy
|
||||||
|
public void wakeUp(int duration_minutes, boolean force) {
|
||||||
|
// If it has been longer than n minutes, do wakeup. Otherwise assume pump is still awake.
|
||||||
|
// **** FIXME: this wakeup doesn't seem to work well... must revisit
|
||||||
|
// receiverDeviceAwakeForMinutes = duration_minutes;
|
||||||
|
|
||||||
|
MedtronicUtil.setPumpDeviceState(PumpDeviceState.WakingUp);
|
||||||
|
|
||||||
|
if (force)
|
||||||
|
nextWakeUpRequired = 0L;
|
||||||
|
|
||||||
|
if (System.currentTimeMillis() > nextWakeUpRequired) {
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.info("Waking pump...");
|
||||||
|
|
||||||
|
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData); // simple
|
||||||
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)200,
|
||||||
|
(byte)0, (byte)0, 25000, (byte)0);
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.info("wakeup: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
|
||||||
|
// FIXME wakeUp successful !!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
nextWakeUpRequired = System.currentTimeMillis() + (receiverDeviceAwakeForMinutes * 60 * 1000);
|
||||||
|
} else {
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.trace("Last pump communication was recent, not waking pump.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// long lastGoodPlus = getLastGoodReceiverCommunicationTime() + (receiverDeviceAwakeForMinutes * 60 * 1000);
|
||||||
|
//
|
||||||
|
// if (System.currentTimeMillis() > lastGoodPlus || force) {
|
||||||
|
// LOG.info("Waking pump...");
|
||||||
|
//
|
||||||
|
// byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.PowerOn);
|
||||||
|
// RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte) 0, (byte) 200, (byte)
|
||||||
|
// 0, (byte) 0, 15000, (byte) 0);
|
||||||
|
// LOG.info("wakeup: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
// } else {
|
||||||
|
// LOG.trace("Last pump communication was recent, not waking pump.");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setRadioFrequencyForPump(double freqMHz) {
|
||||||
|
rfspy.setBaseFrequency(freqMHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double tuneForDevice() {
|
||||||
|
return scanForDevice(RileyLinkUtil.getRileyLinkTargetFrequency().getScanFrequencies());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If user changes pump and one pump is running in US freq, and other in WW, then previously set frequency would be
|
||||||
|
* invalid,
|
||||||
|
* so we would need to retune. This checks that saved frequency is correct range.
|
||||||
|
*
|
||||||
|
* @param frequency
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isValidFrequency(double frequency) {
|
||||||
|
|
||||||
|
double[] scanFrequencies = RileyLinkUtil.getRileyLinkTargetFrequency().getScanFrequencies();
|
||||||
|
|
||||||
|
if (scanFrequencies.length == 1) {
|
||||||
|
return RileyLinkUtil.isSame(scanFrequencies[0], frequency);
|
||||||
|
} else {
|
||||||
|
return (scanFrequencies[0] <= frequency && scanFrequencies[scanFrequencies.length - 1] >= frequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do device connection, with wakeup
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public abstract boolean tryToConnectToDevice();
|
||||||
|
|
||||||
|
|
||||||
|
public double scanForDevice(double[] frequencies) {
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.info("Scanning for receiver ({})", receiverDeviceID);
|
||||||
|
wakeUp(receiverDeviceAwakeForMinutes, false);
|
||||||
|
FrequencyScanResults results = new FrequencyScanResults();
|
||||||
|
|
||||||
|
for (int i = 0; i < frequencies.length; i++) {
|
||||||
|
int tries = 3;
|
||||||
|
FrequencyTrial trial = new FrequencyTrial();
|
||||||
|
trial.frequencyMHz = frequencies[i];
|
||||||
|
rfspy.setBaseFrequency(frequencies[i]);
|
||||||
|
|
||||||
|
int sumRSSI = 0;
|
||||||
|
for (int j = 0; j < tries; j++) {
|
||||||
|
|
||||||
|
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData);
|
||||||
|
RFSpyResponse resp = rfspy.transmitThenReceive(new RadioPacket(pumpMsgContent), (byte)0, (byte)0,
|
||||||
|
(byte)0, (byte)0, 1250, (byte)0);
|
||||||
|
if (resp.wasTimeout()) {
|
||||||
|
LOG.error("scanForPump: Failed to find pump at frequency {}", frequencies[i]);
|
||||||
|
} else if (resp.looksLikeRadioPacket()) {
|
||||||
|
RadioResponse radioResponse = new RadioResponse();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
radioResponse.init(resp.getRaw());
|
||||||
|
|
||||||
|
if (radioResponse.isValid()) {
|
||||||
|
int rssi = calculateRssi(radioResponse.rssi);
|
||||||
|
sumRSSI += rssi;
|
||||||
|
trial.rssiList.add(rssi);
|
||||||
|
trial.successes++;
|
||||||
|
} else {
|
||||||
|
LOG.warn("Failed to parse radio response: " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
trial.rssiList.add(-99);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (RileyLinkCommunicationException rle) {
|
||||||
|
LOG.warn("Failed to decode radio response: " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
trial.rssiList.add(-99);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LOG.error("scanForPump: raw response is " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
trial.rssiList.add(-99);
|
||||||
|
}
|
||||||
|
trial.tries++;
|
||||||
|
}
|
||||||
|
sumRSSI += -99.0 * (trial.tries - trial.successes);
|
||||||
|
trial.averageRSSI2 = (double)(sumRSSI) / (double)(trial.tries);
|
||||||
|
|
||||||
|
trial.calculateAverage();
|
||||||
|
|
||||||
|
results.trials.add(trial);
|
||||||
|
}
|
||||||
|
|
||||||
|
results.dateTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
StringBuilder stringBuilder = new StringBuilder("Scan results:\n");
|
||||||
|
|
||||||
|
for (int k = 0; k < results.trials.size(); k++) {
|
||||||
|
FrequencyTrial one = results.trials.get(k);
|
||||||
|
|
||||||
|
stringBuilder.append(String.format("Scan Result[%s]: Freq=%s, avg RSSI = %s\n", "" + k, ""
|
||||||
|
+ one.frequencyMHz, "" + one.averageRSSI + ", RSSIs =" + one.rssiList));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info(stringBuilder.toString());
|
||||||
|
|
||||||
|
results.sort(); // sorts in ascending order
|
||||||
|
|
||||||
|
FrequencyTrial bestTrial = results.trials.get(results.trials.size() - 1);
|
||||||
|
results.bestFrequencyMHz = bestTrial.frequencyMHz;
|
||||||
|
if (bestTrial.successes > 0) {
|
||||||
|
rfspy.setBaseFrequency(results.bestFrequencyMHz);
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.debug("Best frequency found: " + results.bestFrequencyMHz);
|
||||||
|
return results.bestFrequencyMHz;
|
||||||
|
} else {
|
||||||
|
LOG.error("No pump response during scan.");
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int calculateRssi(int rssiIn) {
|
||||||
|
int rssiOffset = 73;
|
||||||
|
int outRssi = 0;
|
||||||
|
if (rssiIn >= 128) {
|
||||||
|
outRssi = ((rssiIn - 256) / 2) - rssiOffset;
|
||||||
|
} else {
|
||||||
|
outRssi = (rssiIn / 2) - rssiOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outRssi;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public abstract byte[] createPumpMessageContent(RLMessageType type);
|
||||||
|
|
||||||
|
|
||||||
|
private int tune_tryFrequency(double freqMHz) {
|
||||||
|
rfspy.setBaseFrequency(freqMHz);
|
||||||
|
// RLMessage msg = makeRLMessage(RLMessageType.ReadSimpleData);
|
||||||
|
byte[] pumpMsgContent = createPumpMessageContent(RLMessageType.ReadSimpleData);
|
||||||
|
RadioPacket pkt = new RadioPacket(pumpMsgContent);
|
||||||
|
RFSpyResponse resp = rfspy.transmitThenReceive(pkt, (byte)0, (byte)0, (byte)0, (byte)0, SCAN_TIMEOUT, (byte)0);
|
||||||
|
if (resp.wasTimeout()) {
|
||||||
|
LOG.warn("tune_tryFrequency: no pump response at frequency {}", freqMHz);
|
||||||
|
} else if (resp.looksLikeRadioPacket()) {
|
||||||
|
RadioResponse radioResponse = new RadioResponse();
|
||||||
|
try {
|
||||||
|
radioResponse.init(resp.getRaw());
|
||||||
|
|
||||||
|
if (radioResponse.isValid()) {
|
||||||
|
LOG.warn("tune_tryFrequency: saw response level {} at frequency {}", radioResponse.rssi, freqMHz);
|
||||||
|
return calculateRssi(radioResponse.rssi);
|
||||||
|
} else {
|
||||||
|
LOG.warn("tune_tryFrequency: invalid radio response:"
|
||||||
|
+ ByteUtil.shortHexString(radioResponse.getPayload()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (RileyLinkCommunicationException e) {
|
||||||
|
LOG.warn("Failed to decode radio response: " + ByteUtil.shortHexString(resp.getRaw()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double quickTuneForPump(double startFrequencyMHz) {
|
||||||
|
double betterFrequency = startFrequencyMHz;
|
||||||
|
double stepsize = 0.05;
|
||||||
|
for (int tries = 0; tries < 4; tries++) {
|
||||||
|
double evenBetterFrequency = quickTunePumpStep(betterFrequency, stepsize);
|
||||||
|
if (evenBetterFrequency == 0.0) {
|
||||||
|
// could not see the pump at all.
|
||||||
|
// Try again at larger step size
|
||||||
|
stepsize += 0.05;
|
||||||
|
} else {
|
||||||
|
if ((int)(evenBetterFrequency * 100) == (int)(betterFrequency * 100)) {
|
||||||
|
// value did not change, so we're done.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
betterFrequency = evenBetterFrequency; // and go again.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (betterFrequency == 0.0) {
|
||||||
|
// we've failed... caller should try a full scan for pump
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.error("quickTuneForPump: failed to find pump");
|
||||||
|
} else {
|
||||||
|
rfspy.setBaseFrequency(betterFrequency);
|
||||||
|
if (betterFrequency != startFrequencyMHz) {
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.info("quickTuneForPump: new frequency is {}MHz", betterFrequency);
|
||||||
|
} else {
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.info("quickTuneForPump: pump frequency is the same: {}MHz", startFrequencyMHz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return betterFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private double quickTunePumpStep(double startFrequencyMHz, double stepSizeMHz) {
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.info("Doing quick radio tune for receiver ({})", receiverDeviceID);
|
||||||
|
wakeUp(false);
|
||||||
|
int startRssi = tune_tryFrequency(startFrequencyMHz);
|
||||||
|
double lowerFrequency = startFrequencyMHz - stepSizeMHz;
|
||||||
|
int lowerRssi = tune_tryFrequency(lowerFrequency);
|
||||||
|
double higherFrequency = startFrequencyMHz + stepSizeMHz;
|
||||||
|
int higherRssi = tune_tryFrequency(higherFrequency);
|
||||||
|
|
||||||
|
if ((higherRssi == 0.0) && (lowerRssi == 0.0) && (startRssi == 0.0)) {
|
||||||
|
// we can't see the pump at all...
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
if (higherRssi > startRssi) {
|
||||||
|
// need to move higher
|
||||||
|
return higherFrequency;
|
||||||
|
} else if (lowerRssi > startRssi) {
|
||||||
|
// need to move lower.
|
||||||
|
return lowerFrequency;
|
||||||
|
}
|
||||||
|
return startFrequencyMHz;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void rememberLastGoodDeviceCommunicationTime() {
|
||||||
|
lastGoodReceiverCommunicationTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
SP.putLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, lastGoodReceiverCommunicationTime);
|
||||||
|
pumpStatus.setLastCommunicationToNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private long getLastGoodReceiverCommunicationTime() {
|
||||||
|
// If we have a value of zero, we need to load from prefs.
|
||||||
|
if (lastGoodReceiverCommunicationTime == 0L) {
|
||||||
|
lastGoodReceiverCommunicationTime = SP.getLong(RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L);
|
||||||
|
// Might still be zero, but that's fine.
|
||||||
|
}
|
||||||
|
double minutesAgo = (System.currentTimeMillis() - lastGoodReceiverCommunicationTime) / (1000.0 * 60.0);
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.trace("Last good pump communication was " + minutesAgo + " minutes ago.");
|
||||||
|
return lastGoodReceiverCommunicationTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PumpStatus getPumpStatus() {
|
||||||
|
return pumpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void clearNotConnectedCount() {
|
||||||
|
if (rfspy != null) {
|
||||||
|
rfspy.notConnectedCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isLogEnabled() {
|
||||||
|
return L.isEnabled(L.PUMPCOMM);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.hw.rileylink;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 16/05/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RileyLinkConst {
|
||||||
|
|
||||||
|
static final String Prefix = "AAPS.RileyLink.";
|
||||||
|
|
||||||
|
public class Intents {
|
||||||
|
|
||||||
|
public static final String RileyLinkReady = Prefix + "RileyLink_Ready";
|
||||||
|
public static final String RileyLinkGattFailed = Prefix + "RileyLink_Gatt_Failed";
|
||||||
|
|
||||||
|
public static final String BluetoothConnected = Prefix + "Bluetooth_Connected";
|
||||||
|
public static final String BluetoothReconnected = Prefix + "Bluetooth_Reconnected";
|
||||||
|
public static final String BluetoothDisconnected = Prefix + "Bluetooth_Disconnected";
|
||||||
|
public static final String RileyLinkDisconnected = Prefix + "RileyLink_Disconnected";
|
||||||
|
|
||||||
|
public static final String RileyLinkNewAddressSet = Prefix + "NewAddressSet";
|
||||||
|
|
||||||
|
public static final String INTENT_NEW_rileylinkAddressKey = Prefix + "INTENT_NEW_rileylinkAddressKey";
|
||||||
|
public static final String INTENT_NEW_pumpIDKey = Prefix + "INTENT_NEW_pumpIDKey";
|
||||||
|
public static final String RileyLinkDisconnect = Prefix + "RileyLink_Disconnect";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Prefs {
|
||||||
|
|
||||||
|
//public static final String PrefPrefix = "pref_rileylink_";
|
||||||
|
//public static final String RileyLinkAddress = PrefPrefix + "mac_address"; // pref_rileylink_mac_address
|
||||||
|
public static final int RileyLinkAddress = R.string.pref_key_rileylink_mac_address;
|
||||||
|
public static final String LastGoodDeviceCommunicationTime = Prefix + "lastGoodDeviceCommunicationTime";
|
||||||
|
public static final String LastGoodDeviceFrequency = Prefix + "LastGoodDeviceFrequency";
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IPC {
|
||||||
|
|
||||||
|
// needs to br renamed (and maybe removed)
|
||||||
|
public static final String MSG_PUMP_quickTune = Prefix + "MSG_PUMP_quickTune";
|
||||||
|
public static final String MSG_PUMP_tunePump = Prefix + "MSG_PUMP_tunePump";
|
||||||
|
public static final String MSG_PUMP_fetchHistory = Prefix + "MSG_PUMP_fetchHistory";
|
||||||
|
public static final String MSG_PUMP_fetchSavedHistory = Prefix + "MSG_PUMP_fetchSavedHistory";
|
||||||
|
|
||||||
|
public static final String MSG_ServiceCommand = Prefix + "MSG_ServiceCommand";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,333 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.hw.rileylink;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkBLE;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding.Encoding4b6b;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.encoding.Encoding4b6bGeoff;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkTargetFrequency;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.BleAdvertisedData;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.data.RLHistoryItem;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkService;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceNotification;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceResult;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTask;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.ui.RileyLinkSelectPreference;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.events.EventMedtronicDeviceStatusChange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by andy on 17/05/2018.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RileyLinkUtil {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
|
||||||
|
protected static List<RLHistoryItem> historyRileyLink = new ArrayList<>();
|
||||||
|
protected static RileyLinkCommunicationManager rileyLinkCommunicationManager;
|
||||||
|
static ServiceTask currentTask;
|
||||||
|
private static Context context;
|
||||||
|
private static RileyLinkBLE rileyLinkBLE;
|
||||||
|
private static RileyLinkServiceData rileyLinkServiceData;
|
||||||
|
private static RileyLinkService rileyLinkService;
|
||||||
|
private static RileyLinkTargetFrequency rileyLinkTargetFrequency;
|
||||||
|
|
||||||
|
private static RileyLinkTargetDevice targetDevice;
|
||||||
|
private static RileyLinkEncodingType encoding;
|
||||||
|
private static RileyLinkSelectPreference rileyLinkSelectPreference;
|
||||||
|
private static Encoding4b6b encoding4b6b;
|
||||||
|
private static RileyLinkFirmwareVersion firmwareVersion;
|
||||||
|
|
||||||
|
|
||||||
|
public static void setContext(Context contextIn) {
|
||||||
|
RileyLinkUtil.context = contextIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkEncodingType getEncoding() {
|
||||||
|
return encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setEncoding(RileyLinkEncodingType encoding) {
|
||||||
|
RileyLinkUtil.encoding = encoding;
|
||||||
|
|
||||||
|
if (encoding == RileyLinkEncodingType.FourByteSixByteLocal) {
|
||||||
|
RileyLinkUtil.encoding4b6b = new Encoding4b6bGeoff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void sendBroadcastMessage(String message) {
|
||||||
|
if (context != null) {
|
||||||
|
Intent intent = new Intent(message);
|
||||||
|
LocalBroadcastManager.getInstance(RileyLinkUtil.context).sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setServiceState(RileyLinkServiceState newState) {
|
||||||
|
setServiceState(newState, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkError getError() {
|
||||||
|
if (RileyLinkUtil.rileyLinkServiceData != null)
|
||||||
|
return RileyLinkUtil.rileyLinkServiceData.errorCode;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkServiceState getServiceState() {
|
||||||
|
return workWithServiceState(null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setServiceState(RileyLinkServiceState newState, RileyLinkError errorCode) {
|
||||||
|
workWithServiceState(newState, errorCode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static synchronized RileyLinkServiceState workWithServiceState(RileyLinkServiceState newState,
|
||||||
|
RileyLinkError errorCode, boolean set) {
|
||||||
|
|
||||||
|
if (set) {
|
||||||
|
|
||||||
|
RileyLinkUtil.rileyLinkServiceData.serviceState = newState;
|
||||||
|
RileyLinkUtil.rileyLinkServiceData.errorCode = errorCode;
|
||||||
|
|
||||||
|
if (L.isEnabled(L.PUMP))
|
||||||
|
LOG.info("RileyLink State Changed: {} {}", newState, errorCode == null ? "" : " - Error State: "
|
||||||
|
+ errorCode.name());
|
||||||
|
|
||||||
|
RileyLinkUtil.historyRileyLink.add(new RLHistoryItem(RileyLinkUtil.rileyLinkServiceData.serviceState,
|
||||||
|
RileyLinkUtil.rileyLinkServiceData.errorCode, targetDevice));
|
||||||
|
MainApp.bus().post(new EventMedtronicDeviceStatusChange(newState, errorCode));
|
||||||
|
return null;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return (RileyLinkUtil.rileyLinkServiceData == null || RileyLinkUtil.rileyLinkServiceData.serviceState == null) ? //
|
||||||
|
RileyLinkServiceState.NotStarted
|
||||||
|
: RileyLinkUtil.rileyLinkServiceData.serviceState;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkBLE getRileyLinkBLE() {
|
||||||
|
return RileyLinkUtil.rileyLinkBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setRileyLinkBLE(RileyLinkBLE rileyLinkBLEIn) {
|
||||||
|
RileyLinkUtil.rileyLinkBLE = rileyLinkBLEIn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkServiceData getRileyLinkServiceData() {
|
||||||
|
return RileyLinkUtil.rileyLinkServiceData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setRileyLinkServiceData(RileyLinkServiceData rileyLinkServiceData) {
|
||||||
|
RileyLinkUtil.rileyLinkServiceData = rileyLinkServiceData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean hasPumpBeenTunned() {
|
||||||
|
return RileyLinkUtil.rileyLinkServiceData.tuneUpDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkService getRileyLinkService() {
|
||||||
|
return RileyLinkUtil.rileyLinkService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setRileyLinkService(RileyLinkService rileyLinkService) {
|
||||||
|
RileyLinkUtil.rileyLinkService = rileyLinkService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkCommunicationManager getRileyLinkCommunicationManager() {
|
||||||
|
return RileyLinkUtil.rileyLinkCommunicationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setRileyLinkCommunicationManager(RileyLinkCommunicationManager rileyLinkCommunicationManager) {
|
||||||
|
RileyLinkUtil.rileyLinkCommunicationManager = rileyLinkCommunicationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean sendNotification(ServiceNotification notification, Integer clientHashcode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME remove ?
|
||||||
|
public static void setCurrentTask(ServiceTask task) {
|
||||||
|
if (currentTask == null) {
|
||||||
|
currentTask = task;
|
||||||
|
} else {
|
||||||
|
//LOG.error("setCurrentTask: Cannot replace current task");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void finishCurrentTask(ServiceTask task) {
|
||||||
|
if (task != currentTask) {
|
||||||
|
//LOG.error("finishCurrentTask: task does not match");
|
||||||
|
}
|
||||||
|
// hack to force deep copy of transport contents
|
||||||
|
ServiceTransport transport = task.getServiceTransport().clone();
|
||||||
|
|
||||||
|
if (transport.hasServiceResult()) {
|
||||||
|
sendServiceTransportResponse(transport, transport.getServiceResult());
|
||||||
|
}
|
||||||
|
currentTask = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void sendServiceTransportResponse(ServiceTransport transport, ServiceResult serviceResult) {
|
||||||
|
// get the key (hashcode) of the client who requested this
|
||||||
|
Integer clientHashcode = transport.getSenderHashcode();
|
||||||
|
// make a new bundle to send as the message data
|
||||||
|
transport.setServiceResult(serviceResult);
|
||||||
|
// FIXME
|
||||||
|
// transport.setTransportType(RT2Const.IPC.MSG_ServiceResult);
|
||||||
|
// rileyLinkIPCConnection.sendTransport(transport, clientHashcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkTargetFrequency getRileyLinkTargetFrequency() {
|
||||||
|
return RileyLinkUtil.rileyLinkTargetFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setRileyLinkTargetFrequency(RileyLinkTargetFrequency rileyLinkTargetFrequency) {
|
||||||
|
RileyLinkUtil.rileyLinkTargetFrequency = rileyLinkTargetFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isSame(Double d1, Double d2) {
|
||||||
|
double diff = d1 - d2;
|
||||||
|
|
||||||
|
return (Math.abs(diff) <= 0.000001);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static BleAdvertisedData parseAdertisedData(byte[] advertisedData) {
|
||||||
|
List<UUID> uuids = new ArrayList<UUID>();
|
||||||
|
String name = null;
|
||||||
|
if (advertisedData == null) {
|
||||||
|
return new BleAdvertisedData(uuids, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.wrap(advertisedData).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
while (buffer.remaining() > 2) {
|
||||||
|
byte length = buffer.get();
|
||||||
|
if (length == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
byte type = buffer.get();
|
||||||
|
switch (type) {
|
||||||
|
case 0x02: // Partial list of 16-bit UUIDs
|
||||||
|
case 0x03: // Complete list of 16-bit UUIDs
|
||||||
|
while (length >= 2) {
|
||||||
|
uuids
|
||||||
|
.add(UUID.fromString(String.format("%08x-0000-1000-8000-00805f9b34fb", buffer.getShort())));
|
||||||
|
length -= 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x06: // Partial list of 128-bit UUIDs
|
||||||
|
case 0x07: // Complete list of 128-bit UUIDs
|
||||||
|
while (length >= 16) {
|
||||||
|
long lsb = buffer.getLong();
|
||||||
|
long msb = buffer.getLong();
|
||||||
|
uuids.add(new UUID(msb, lsb));
|
||||||
|
length -= 16;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 0x09:
|
||||||
|
byte[] nameBytes = new byte[length - 1];
|
||||||
|
buffer.get(nameBytes);
|
||||||
|
try {
|
||||||
|
name = new String(nameBytes, "utf-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
buffer.position(buffer.position() + length - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new BleAdvertisedData(uuids, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static List<RLHistoryItem> getRileyLinkHistory() {
|
||||||
|
return historyRileyLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkTargetDevice getTargetDevice() {
|
||||||
|
return targetDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setTargetDevice(RileyLinkTargetDevice targetDevice) {
|
||||||
|
RileyLinkUtil.targetDevice = targetDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setRileyLinkSelectPreference(RileyLinkSelectPreference rileyLinkSelectPreference) {
|
||||||
|
|
||||||
|
RileyLinkUtil.rileyLinkSelectPreference = rileyLinkSelectPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkSelectPreference getRileyLinkSelectPreference() {
|
||||||
|
|
||||||
|
return rileyLinkSelectPreference;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Encoding4b6b getEncoding4b6b() {
|
||||||
|
return RileyLinkUtil.encoding4b6b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void setFirmwareVersion(RileyLinkFirmwareVersion firmwareVersion) {
|
||||||
|
RileyLinkUtil.firmwareVersion = firmwareVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static RileyLinkFirmwareVersion getFirmwareVersion() {
|
||||||
|
return firmwareVersion;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,438 @@
|
||||||
|
package info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble;
|
||||||
|
|
||||||
|
import android.os.SystemClock;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.MainApp;
|
||||||
|
import info.nightscout.androidaps.R;
|
||||||
|
import info.nightscout.androidaps.logging.L;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.Reset;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.RileyLinkCommand;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.SendAndListen;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.SetHardwareEncoding;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.SetPreamble;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.command.UpdateRegister;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.GattAttributes;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RFSpyResponse;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.data.RadioPacket;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.CC111XRegister;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RXFilterMode;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkCommandType;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkEncodingType;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkFirmwareVersion;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.RileyLinkTargetFrequency;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.operations.BLECommOperationResult;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.StringUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.common.utils.ThreadUtil;
|
||||||
|
import info.nightscout.androidaps.plugins.pump.medtronic.util.MedtronicConst;
|
||||||
|
import info.nightscout.androidaps.utils.SP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by geoff on 5/26/16.
|
||||||
|
*/
|
||||||
|
public class RFSpy {
|
||||||
|
|
||||||
|
public static final long RILEYLINK_FREQ_XTAL = 24000000;
|
||||||
|
public static final int EXPECTED_MAX_BLUETOOTH_LATENCY_MS = 7500; // 1500
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(L.PUMPBTCOMM);
|
||||||
|
public int notConnectedCount = 0;
|
||||||
|
private RileyLinkBLE rileyLinkBle;
|
||||||
|
private RFSpyReader reader;
|
||||||
|
private RileyLinkTargetFrequency selectedTargetFrequency;
|
||||||
|
private UUID radioServiceUUID = UUID.fromString(GattAttributes.SERVICE_RADIO);
|
||||||
|
private UUID radioDataUUID = UUID.fromString(GattAttributes.CHARA_RADIO_DATA);
|
||||||
|
private UUID radioVersionUUID = UUID.fromString(GattAttributes.CHARA_RADIO_VERSION);
|
||||||
|
private UUID responseCountUUID = UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT);
|
||||||
|
private RileyLinkFirmwareVersion firmwareVersion;
|
||||||
|
private String bleVersion; // We don't use it so no need of sofisticated logic
|
||||||
|
Double currentFrequencyMHz;
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpy(RileyLinkBLE rileyLinkBle) {
|
||||||
|
this.rileyLinkBle = rileyLinkBle;
|
||||||
|
reader = new RFSpyReader(rileyLinkBle);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RileyLinkFirmwareVersion getRLVersionCached() {
|
||||||
|
return firmwareVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getBLEVersionCached() {
|
||||||
|
return bleVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Call this after the RL services are discovered.
|
||||||
|
// Starts an async task to read when data is available
|
||||||
|
public void startReader() {
|
||||||
|
rileyLinkBle.registerRadioResponseCountNotification(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
newDataIsAvailable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reader.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Here should go generic RL initialisation + protocol adjustments depending on
|
||||||
|
// firmware version
|
||||||
|
public void initializeRileyLink() {
|
||||||
|
bleVersion = getVersion();
|
||||||
|
firmwareVersion = getFirmwareVersion();
|
||||||
|
RileyLinkUtil.setFirmwareVersion(firmwareVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Call this from the "response count" notification handler.
|
||||||
|
public void newDataIsAvailable() {
|
||||||
|
// pass the message to the reader (which should be internal to RFSpy)
|
||||||
|
reader.newDataIsAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This gets the version from the BLE113, not from the CC1110.
|
||||||
|
// I.e., this gets the version from the BLE interface, not from the radio.
|
||||||
|
public String getVersion() {
|
||||||
|
BLECommOperationResult result = rileyLinkBle.readCharacteristic_blocking(radioServiceUUID, radioVersionUUID);
|
||||||
|
if (result.resultCode == BLECommOperationResult.RESULT_SUCCESS) {
|
||||||
|
String version = StringUtil.fromBytes(result.value);
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.debug("BLE Version: " + version);
|
||||||
|
return version;
|
||||||
|
} else {
|
||||||
|
LOG.error("getVersion failed with code: " + result.resultCode);
|
||||||
|
return "(null)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RileyLinkFirmwareVersion getFirmwareVersion() {
|
||||||
|
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.debug("Firmware Version. Get Version - Start");
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
// We have to call raw version of communication to get firmware version
|
||||||
|
// So that we can adjust other commands accordingly afterwords
|
||||||
|
|
||||||
|
byte[] getVersionRaw = getByteArray(RileyLinkCommandType.GetVersion.code);
|
||||||
|
byte[] response = writeToDataRaw(getVersionRaw, 5000);
|
||||||
|
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.debug("Firmware Version. GetVersion [response={}]", ByteUtil.shortHexString(response));
|
||||||
|
|
||||||
|
if (response != null) { // && response[0] == (byte) 0xDD) {
|
||||||
|
|
||||||
|
String versionString = StringUtil.fromBytes(response);
|
||||||
|
|
||||||
|
RileyLinkFirmwareVersion version = RileyLinkFirmwareVersion.getByVersionString(StringUtil
|
||||||
|
.fromBytes(response));
|
||||||
|
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.trace("Firmware Version string: {}, resolved to {}.", versionString, version);
|
||||||
|
|
||||||
|
if (version != RileyLinkFirmwareVersion.UnknownVersion)
|
||||||
|
return version;
|
||||||
|
|
||||||
|
SystemClock.sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.error("Firmware Version can't be determined. Checking with BLE Version [{}].", bleVersion);
|
||||||
|
|
||||||
|
if (bleVersion.contains(" 2.")) {
|
||||||
|
return RileyLinkFirmwareVersion.Version_2_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RileyLinkFirmwareVersion.UnknownVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] writeToDataRaw(byte[] bytes, int responseTimeout_ms) {
|
||||||
|
SystemClock.sleep(100);
|
||||||
|
// FIXME drain read queue?
|
||||||
|
byte[] junkInBuffer = reader.poll(0);
|
||||||
|
|
||||||
|
while (junkInBuffer != null) {
|
||||||
|
LOG.warn(ThreadUtil.sig() + "writeToData: draining read queue, found this: "
|
||||||
|
+ ByteUtil.shortHexString(junkInBuffer));
|
||||||
|
junkInBuffer = reader.poll(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepend length, and send it.
|
||||||
|
byte[] prepended = ByteUtil.concat(new byte[]{(byte) (bytes.length)}, bytes);
|
||||||
|
|
||||||
|
LOG.debug("writeToData (raw={})", ByteUtil.shortHexString(prepended));
|
||||||
|
|
||||||
|
BLECommOperationResult writeCheck = rileyLinkBle.writeCharacteristic_blocking(radioServiceUUID, radioDataUUID,
|
||||||
|
prepended);
|
||||||
|
if (writeCheck.resultCode != BLECommOperationResult.RESULT_SUCCESS) {
|
||||||
|
LOG.error("BLE Write operation failed, code=" + writeCheck.resultCode);
|
||||||
|
return null; // will be a null (invalid) response
|
||||||
|
}
|
||||||
|
SystemClock.sleep(100);
|
||||||
|
// Log.i(TAG,ThreadUtil.sig()+String.format(" writeToData:(timeout %d) %s",(responseTimeout_ms),ByteUtil.shortHexString(prepended)));
|
||||||
|
byte[] rawResponse = reader.poll(responseTimeout_ms);
|
||||||
|
return rawResponse;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The caller has to know how long the RFSpy will be busy with what was sent to it.
|
||||||
|
private RFSpyResponse writeToData(RileyLinkCommand command, int responseTimeout_ms) {
|
||||||
|
|
||||||
|
byte[] bytes = command.getRaw();
|
||||||
|
byte[] rawResponse = writeToDataRaw(bytes, responseTimeout_ms);
|
||||||
|
|
||||||
|
RFSpyResponse resp = new RFSpyResponse(command, rawResponse);
|
||||||
|
if (rawResponse == null) {
|
||||||
|
LOG.error("writeToData: No response from RileyLink");
|
||||||
|
notConnectedCount++;
|
||||||
|
} else {
|
||||||
|
if (resp.wasInterrupted()) {
|
||||||
|
LOG.error("writeToData: RileyLink was interrupted");
|
||||||
|
} else if (resp.wasTimeout()) {
|
||||||
|
LOG.error("writeToData: RileyLink reports timeout");
|
||||||
|
notConnectedCount++;
|
||||||
|
} else if (resp.isOK()) {
|
||||||
|
LOG.warn("writeToData: RileyLink reports OK");
|
||||||
|
resetNotConnectedCount();
|
||||||
|
} else {
|
||||||
|
if (resp.looksLikeRadioPacket()) {
|
||||||
|
// RadioResponse radioResp = resp.getRadioResponse();
|
||||||
|
// byte[] responsePayload = radioResp.getPayload();
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.trace("writeToData: received radio response. Will decode at upper level");
|
||||||
|
resetNotConnectedCount();
|
||||||
|
}
|
||||||
|
// Log.i(TAG, "writeToData: raw response is " + ByteUtil.shortHexString(rawResponse));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void resetNotConnectedCount() {
|
||||||
|
this.notConnectedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] getByteArray(byte... input) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private byte[] getCommandArray(RileyLinkCommandType command, byte[] body) {
|
||||||
|
int bodyLength = body == null ? 0 : body.length;
|
||||||
|
|
||||||
|
byte[] output = new byte[bodyLength + 1];
|
||||||
|
|
||||||
|
output[0] = command.code;
|
||||||
|
|
||||||
|
if (body != null) {
|
||||||
|
for (int i = 0; i < body.length; i++) {
|
||||||
|
output[i + 1] = body[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpyResponse transmitThenReceive(RadioPacket pkt, byte sendChannel, byte repeatCount, byte delay_ms,
|
||||||
|
byte listenChannel, int timeout_ms, byte retryCount) {
|
||||||
|
return transmitThenReceive(pkt, sendChannel, repeatCount, delay_ms, listenChannel, timeout_ms, retryCount, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpyResponse transmitThenReceive(RadioPacket pkt, int timeout_ms) {
|
||||||
|
return transmitThenReceive(pkt, (byte) 0, (byte) 0, (byte) 0, (byte) 0, timeout_ms, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpyResponse transmitThenReceive(RadioPacket pkt, byte sendChannel, byte repeatCount, byte delay_ms,
|
||||||
|
byte listenChannel, int timeout_ms, byte retryCount, Integer extendPreamble_ms) {
|
||||||
|
|
||||||
|
int sendDelay = repeatCount * delay_ms;
|
||||||
|
int receiveDelay = timeout_ms * (retryCount + 1);
|
||||||
|
|
||||||
|
SendAndListen command = new SendAndListen(sendChannel, repeatCount, delay_ms, listenChannel, timeout_ms,
|
||||||
|
retryCount, extendPreamble_ms, pkt);
|
||||||
|
|
||||||
|
return writeToData(command, sendDelay + receiveDelay + EXPECTED_MAX_BLUETOOTH_LATENCY_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpyResponse updateRegister(CC111XRegister reg, int val) {
|
||||||
|
RFSpyResponse resp = writeToData(new UpdateRegister(reg, (byte) val), EXPECTED_MAX_BLUETOOTH_LATENCY_MS);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setBaseFrequency(double freqMHz) {
|
||||||
|
int value = (int) (freqMHz * 1000000 / ((double) (RILEYLINK_FREQ_XTAL) / Math.pow(2.0, 16.0)));
|
||||||
|
updateRegister(CC111XRegister.freq0, (byte) (value & 0xff));
|
||||||
|
updateRegister(CC111XRegister.freq1, (byte) ((value >> 8) & 0xff));
|
||||||
|
updateRegister(CC111XRegister.freq2, (byte) ((value >> 16) & 0xff));
|
||||||
|
LOG.info("Set frequency to {} MHz", freqMHz);
|
||||||
|
|
||||||
|
this.currentFrequencyMHz = freqMHz;
|
||||||
|
|
||||||
|
configureRadioForRegion(RileyLinkUtil.getRileyLinkTargetFrequency());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void configureRadioForRegion(RileyLinkTargetFrequency frequency) {
|
||||||
|
|
||||||
|
// we update registers only on first run, or if region changed
|
||||||
|
|
||||||
|
switch (frequency) {
|
||||||
|
case Medtronic_WorldWide: {
|
||||||
|
// updateRegister(CC111X_MDMCFG4, (byte) 0x59);
|
||||||
|
setRXFilterMode(RXFilterMode.Wide);
|
||||||
|
// updateRegister(CC111X_MDMCFG3, (byte) 0x66);
|
||||||
|
// updateRegister(CC111X_MDMCFG2, (byte) 0x33);
|
||||||
|
updateRegister(CC111XRegister.mdmcfg1, 0x62);
|
||||||
|
updateRegister(CC111XRegister.mdmcfg0, 0x1A);
|
||||||
|
updateRegister(CC111XRegister.deviatn, 0x13);
|
||||||
|
setMedtronicEncoding();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Medtronic_US: {
|
||||||
|
// updateRegister(CC111X_MDMCFG4, (byte) 0x99);
|
||||||
|
setRXFilterMode(RXFilterMode.Narrow);
|
||||||
|
// updateRegister(CC111X_MDMCFG3, (byte) 0x66);
|
||||||
|
// updateRegister(CC111X_MDMCFG2, (byte) 0x33);
|
||||||
|
updateRegister(CC111XRegister.mdmcfg1, 0x61);
|
||||||
|
updateRegister(CC111XRegister.mdmcfg0, 0x7E);
|
||||||
|
updateRegister(CC111XRegister.deviatn, 0x15);
|
||||||
|
setMedtronicEncoding();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Omnipod: {
|
||||||
|
RFSpyResponse r = null;
|
||||||
|
// RL initialization for Omnipod is a copy/paste from OmniKit implementation.
|
||||||
|
// Last commit from original repository: 5c3beb4144
|
||||||
|
// so if something is terribly wrong, please check git diff PodCommsSession.swift since that commit
|
||||||
|
r = updateRegister(CC111XRegister.pktctrl1, 0x20);
|
||||||
|
r = updateRegister(CC111XRegister.agcctrl0, 0x00);
|
||||||
|
r = updateRegister(CC111XRegister.fsctrl1, 0x06);
|
||||||
|
r = updateRegister(CC111XRegister.mdmcfg4, 0xCA);
|
||||||
|
r = updateRegister(CC111XRegister.mdmcfg3, 0xBC);
|
||||||
|
r = updateRegister(CC111XRegister.mdmcfg2, 0x06);
|
||||||
|
r = updateRegister(CC111XRegister.mdmcfg1, 0x70);
|
||||||
|
r = updateRegister(CC111XRegister.mdmcfg0, 0x11);
|
||||||
|
r = updateRegister(CC111XRegister.deviatn, 0x44);
|
||||||
|
r = updateRegister(CC111XRegister.mcsm0, 0x18);
|
||||||
|
r = updateRegister(CC111XRegister.foccfg, 0x17);
|
||||||
|
r = updateRegister(CC111XRegister.fscal3, 0xE9);
|
||||||
|
r = updateRegister(CC111XRegister.fscal2, 0x2A);
|
||||||
|
r = updateRegister(CC111XRegister.fscal1, 0x00);
|
||||||
|
r = updateRegister(CC111XRegister.fscal0, 0x1F);
|
||||||
|
|
||||||
|
r = updateRegister(CC111XRegister.test1, 0x31);
|
||||||
|
r = updateRegister(CC111XRegister.test0, 0x09);
|
||||||
|
r = updateRegister(CC111XRegister.paTable0, 0x84);
|
||||||
|
r = updateRegister(CC111XRegister.sync1, 0xA5);
|
||||||
|
r = updateRegister(CC111XRegister.sync0, 0x5A);
|
||||||
|
|
||||||
|
r = setRileyLinkEncoding(RileyLinkEncodingType.Manchester);
|
||||||
|
r = setPreamble(0x6665);
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG.warn("No region configuration for RfSpy and {}", frequency.name());
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedTargetFrequency = frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setMedtronicEncoding() {
|
||||||
|
RileyLinkEncodingType encoding = RileyLinkEncodingType.FourByteSixByteLocal;
|
||||||
|
|
||||||
|
if (RileyLinkFirmwareVersion.isSameVersion(this.firmwareVersion, RileyLinkFirmwareVersion.Version2AndHigher)) {
|
||||||
|
if (SP.getString(MedtronicConst.Prefs.Encoding, "None").equals(MainApp.gs(R.string.medtronic_pump_encoding_4b6b_rileylink))) {
|
||||||
|
encoding = RileyLinkEncodingType.FourByteSixByteRileyLink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setRileyLinkEncoding(encoding);
|
||||||
|
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.debug("Set Encoding for Medtronic: " + encoding.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private RFSpyResponse setPreamble(int preamble) {
|
||||||
|
RFSpyResponse resp = null;
|
||||||
|
try {
|
||||||
|
resp = writeToData(new SetPreamble(preamble), EXPECTED_MAX_BLUETOOTH_LATENCY_MS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.toString();
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpyResponse setRileyLinkEncoding(RileyLinkEncodingType encoding) {
|
||||||
|
RFSpyResponse resp = writeToData(new SetHardwareEncoding(encoding), EXPECTED_MAX_BLUETOOTH_LATENCY_MS);
|
||||||
|
|
||||||
|
if (resp.isOK()) {
|
||||||
|
reader.setRileyLinkEncodingType(encoding);
|
||||||
|
RileyLinkUtil.setEncoding(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setRXFilterMode(RXFilterMode mode) {
|
||||||
|
|
||||||
|
byte drate_e = (byte) 0x9; // exponent of symbol rate (16kbps)
|
||||||
|
byte chanbw = mode.value;
|
||||||
|
|
||||||
|
updateRegister(CC111XRegister.mdmcfg4, (byte) (chanbw | drate_e));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset RileyLink Configuration (set all updateRegisters)
|
||||||
|
*/
|
||||||
|
public void resetRileyLinkConfiguration() {
|
||||||
|
if (this.currentFrequencyMHz != null)
|
||||||
|
this.setBaseFrequency(this.currentFrequencyMHz);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public RFSpyResponse resetRileyLink() {
|
||||||
|
RFSpyResponse resp = null;
|
||||||
|
try {
|
||||||
|
resp = writeToData(new Reset(), EXPECTED_MAX_BLUETOOTH_LATENCY_MS);
|
||||||
|
if (isLogEnabled())
|
||||||
|
LOG.debug("Reset command send, response: {}", resp);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.toString();
|
||||||
|
}
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isLogEnabled() {
|
||||||
|
return L.isEnabled(L.PUMPBTCOMM);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue