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
|
||||
|
||||
before_install:
|
||||
- yes | sdkmanager "platforms;android-28"
|
||||
#- yes | sdkmanager "platforms;android-28"
|
||||
|
||||
script:
|
||||
# Unit Test
|
||||
|
|
|
@ -11,4 +11,4 @@
|
|||
dev: [![codecov](https://codecov.io/gh/MilosKozak/AndroidAPS/branch/dev/graph/badge.svg)](https://codecov.io/gh/MilosKozak/AndroidAPS)
|
||||
|
||||
|
||||
[![Donate via PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y4LHGJJESAVB8)
|
||||
[![Donate via PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Y4LHGJJESAVB8)
|
|
@ -215,6 +215,7 @@ allprojects {
|
|||
flatDir {
|
||||
dirs 'libs'
|
||||
}
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,6 +275,8 @@ dependencies {
|
|||
implementation "com.jakewharton:butterknife:${butterknifeVersion}"
|
||||
annotationProcessor "com.jakewharton:butterknife-compiler:${butterknifeVersion}"
|
||||
|
||||
implementation 'com.github.DavidProdinger:weekdays-selector:1.0.4'
|
||||
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation "org.json:json:20140107"
|
||||
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:${powermockVersion}"
|
||||
testImplementation "joda-time:joda-time:2.9.9"
|
||||
testImplementation "com.google.truth:truth:0.39"
|
||||
testImplementation 'org.robolectric:robolectric:4.2.1'
|
||||
testImplementation("com.google.truth:truth:0.39") {
|
||||
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.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 "com.google.dexmaker:dexmaker:${dexmakerVersion}"
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
<uses-permission android:name="com.google.android.permission.PROVIDE_BACKGROUND" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<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_WIFI_STATE" />
|
||||
<uses-permission android:name="com.dexcom.cgm.EXTERNAL_PERMISSION" />
|
||||
|
@ -161,6 +162,9 @@
|
|||
<service
|
||||
android:name=".services.DataService"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".services.LocationService"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".plugins.pump.danaR.services.DanaRExecutionService"
|
||||
android:enabled="true"
|
||||
|
@ -284,6 +288,23 @@
|
|||
android:theme="@style/AppTheme" />
|
||||
<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"/>
|
||||
|
||||
</application>
|
||||
|
|
|
@ -31,7 +31,7 @@ public class Constants {
|
|||
public static final long remoteBolusMinDistance = 15 * 60 * 1000L;
|
||||
|
||||
// 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_MIN_TIMESHIFT = -6;
|
||||
public static final int CPP_MAX_TIMESHIFT = 23;
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package info.nightscout.androidaps;
|
||||
|
||||
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.res.Resources;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
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.storage.StorageConstraintPlugin;
|
||||
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.food.FoodPlugin;
|
||||
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.simple.SimpleProfilePlugin;
|
||||
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.danaRKorean.DanaRKoreanPlugin;
|
||||
import info.nightscout.androidaps.plugins.pump.danaRS.DanaRSPlugin;
|
||||
import info.nightscout.androidaps.plugins.pump.danaRv2.DanaRv2Plugin;
|
||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin;
|
||||
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.sensitivity.SensitivityAAPSPlugin;
|
||||
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.KeepAliveReceiver;
|
||||
import info.nightscout.androidaps.receivers.NSAlarmReceiver;
|
||||
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver;
|
||||
import info.nightscout.androidaps.services.Intents;
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy;
|
||||
import io.fabric.sdk.android.Fabric;
|
||||
|
@ -113,6 +123,8 @@ public class MainApp extends Application {
|
|||
private static AckAlarmReceiver ackAlarmReciever = new AckAlarmReceiver();
|
||||
private static DBAccessReceiver dbAccessReciever = new DBAccessReceiver();
|
||||
private LocalBroadcastManager lbm;
|
||||
BroadcastReceiver btReceiver;
|
||||
TimeDateOrTZChangeReceiver timeDateOrTZChangeReceiver;
|
||||
|
||||
public static boolean devBranch;
|
||||
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
|
||||
triggerCheckVersion();
|
||||
//setBTReceiver();
|
||||
|
||||
if (pluginsList == null) {
|
||||
pluginsList = new ArrayList<>();
|
||||
|
@ -175,6 +188,7 @@ public class MainApp extends Application {
|
|||
if (Config.PUMPDRIVERS) pluginsList.add(LocalInsightPlugin.getPlugin());
|
||||
pluginsList.add(CareportalPlugin.getPlugin());
|
||||
if (Config.PUMPDRIVERS) pluginsList.add(ComboPlugin.getPlugin());
|
||||
if (Config.PUMPDRIVERS && engineeringMode) pluginsList.add(MedtronicPumpPlugin.getPlugin());
|
||||
if (Config.MDI) pluginsList.add(MDIPlugin.getPlugin());
|
||||
pluginsList.add(VirtualPumpPlugin.getPlugin());
|
||||
if (Config.APS) pluginsList.add(LoopPlugin.getPlugin());
|
||||
|
@ -207,6 +221,8 @@ public class MainApp extends Application {
|
|||
if (engineeringMode)
|
||||
pluginsList.add(TidepoolPlugin.INSTANCE);
|
||||
pluginsList.add(MaintenancePlugin.initPlugin(this));
|
||||
if (engineeringMode)
|
||||
pluginsList.add(AutomationPlugin.INSTANCE);
|
||||
|
||||
pluginsList.add(ConfigBuilderPlugin.getPlugin());
|
||||
|
||||
|
@ -254,6 +270,10 @@ public class MainApp extends Application {
|
|||
|
||||
//register dbaccess
|
||||
lbm.registerReceiver(dbAccessReciever, new IntentFilter(Intents.ACTION_DATABASE));
|
||||
|
||||
this.timeDateOrTZChangeReceiver = new TimeDateOrTZChangeReceiver();
|
||||
this.timeDateOrTZChangeReceiver.registerBroadcasts(this);
|
||||
|
||||
}
|
||||
|
||||
private void startKeepAliveService() {
|
||||
|
@ -439,5 +459,19 @@ public class MainApp extends Application {
|
|||
sDatabaseHelper.close();
|
||||
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.danaRv2.DanaRv2Plugin;
|
||||
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.sensitivity.SensitivityAAPSPlugin;
|
||||
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.OKDialog;
|
||||
import info.nightscout.androidaps.utils.SP;
|
||||
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
|
||||
|
||||
public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
MyPreferenceFragment myPreferenceFragment;
|
||||
|
@ -170,6 +172,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
|
|||
addPreferencesFromResourceIfEnabled(DanaRSPlugin.getPlugin(), PluginType.PUMP);
|
||||
addPreferencesFromResourceIfEnabled(LocalInsightPlugin.getPlugin(), PluginType.PUMP);
|
||||
addPreferencesFromResourceIfEnabled(ComboPlugin.getPlugin(), PluginType.PUMP);
|
||||
addPreferencesFromResourceIfEnabled(MedtronicPumpPlugin.getPlugin(), PluginType.PUMP);
|
||||
|
||||
if (DanaRPlugin.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(TidepoolPlugin.INSTANCE, PluginType.GENERAL);
|
||||
addPreferencesFromResourceIfEnabled(SmsCommunicatorPlugin.getPlugin(), PluginType.GENERAL);
|
||||
addPreferencesFromResourceIfEnabled(AutomationPlugin.INSTANCE, PluginType.GENERAL);
|
||||
|
||||
addPreferencesFromResource(R.xml.pref_others);
|
||||
addPreferencesFromResource(R.xml.pref_datachoices);
|
||||
|
|
|
@ -45,6 +45,11 @@ public class PumpEnactResult {
|
|||
return this;
|
||||
}
|
||||
|
||||
public PumpEnactResult comment(int comment) {
|
||||
this.comment = MainApp.gs(comment);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PumpEnactResult duration(int duration) {
|
||||
this.duration = duration;
|
||||
return this;
|
||||
|
|
|
@ -21,6 +21,8 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
@ -536,6 +538,24 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
|||
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 -------------------
|
||||
|
||||
|
@ -871,6 +891,31 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
|||
log.debug("TEMPBASAL: Already exists from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
|
||||
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);
|
||||
if (L.isEnabled(L.DATABASE))
|
||||
log.debug("TEMPBASAL: New record from: " + Source.getString(tempBasal.source) + " " + tempBasal.toString());
|
||||
|
@ -1111,6 +1156,29 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
|
|||
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 ---------------
|
||||
|
||||
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.LoggerFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import info.nightscout.androidaps.logging.L;
|
||||
import info.nightscout.androidaps.plugins.pump.common.utils.DateTimeUtil;
|
||||
|
||||
/**
|
||||
* Created by mike on 20.09.2017.
|
||||
|
@ -45,4 +44,16 @@ public class TDD {
|
|||
this.basal = basal;
|
||||
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 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.logging.L;
|
||||
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)
|
||||
public class TemporaryBasal implements Interval {
|
||||
public class TemporaryBasal implements Interval, DbObjectBase {
|
||||
private static Logger log = LoggerFactory.getLogger(L.DATABASE);
|
||||
|
||||
@DatabaseField(id = true)
|
||||
|
@ -157,6 +157,14 @@ public class TemporaryBasal implements Interval {
|
|||
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 ---------
|
||||
|
||||
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;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.utils.SP;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -42,6 +45,28 @@ public abstract class PluginBase {
|
|||
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() {
|
||||
// return mainType;
|
||||
// }
|
||||
|
|
|
@ -7,8 +7,10 @@ import java.util.List;
|
|||
import info.nightscout.androidaps.data.DetailedBolusInfo;
|
||||
import info.nightscout.androidaps.data.Profile;
|
||||
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.CustomActionType;
|
||||
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
|
||||
|
||||
/**
|
||||
* Created by mike on 04.06.2016.
|
||||
|
@ -53,8 +55,8 @@ public interface PumpInterface {
|
|||
|
||||
// Status to be passed to NS
|
||||
JSONObject getJSONStatus(Profile profile, String profileName);
|
||||
String manufacter();
|
||||
String model();
|
||||
ManufacturerType manufacturer();
|
||||
PumpType model();
|
||||
String serialNumber();
|
||||
|
||||
// Pump capabilities
|
||||
|
@ -73,4 +75,10 @@ public interface PumpInterface {
|
|||
|
||||
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> getTreatments5MinBackFromHistory(long time);
|
||||
List<Treatment> getTreatmentsFromHistoryAfterTimestamp(long timestamp);
|
||||
long getLastBolusTime();
|
||||
|
||||
// real basals (not faked by extended bolus)
|
||||
|
@ -64,4 +65,4 @@ public interface TreatmentsInterface {
|
|||
|
||||
long oldestDataAvailable();
|
||||
|
||||
}
|
||||
}
|
|
@ -77,6 +77,7 @@ public class L {
|
|||
|
||||
public static final String CORE = "CORE";
|
||||
public static final String AUTOSENS = "AUTOSENS";
|
||||
public static final String AUTOMATION = "AUTOMATION";
|
||||
public static final String EVENTS = "EVENTS";
|
||||
public static final String GLUCOSE = "GLUCOSE";
|
||||
public static final String BGSOURCE = "BGSOURCE";
|
||||
|
@ -97,11 +98,13 @@ public class L {
|
|||
public static final String PROFILE = "PROFILE";
|
||||
public static final String CONFIGBUILDER = "CONFIGBUILDER";
|
||||
public static final String UI = "UI";
|
||||
public static final String LOCATION = "LOCATION";
|
||||
public static final String SMS = "SMS";
|
||||
|
||||
private static void initialize() {
|
||||
logElements = new ArrayList<>();
|
||||
logElements.add(new LogElement(APS, true));
|
||||
logElements.add(new LogElement(AUTOMATION, true));
|
||||
logElements.add(new LogElement(AUTOSENS, false));
|
||||
logElements.add(new LogElement(BGSOURCE, true));
|
||||
logElements.add(new LogElement(GLUCOSE, false));
|
||||
|
@ -113,6 +116,7 @@ public class L {
|
|||
logElements.add(new LogElement(DATASERVICE, true));
|
||||
logElements.add(new LogElement(DATATREATMENTS, true));
|
||||
logElements.add(new LogElement(EVENTS, false, true));
|
||||
logElements.add(new LogElement(LOCATION, true));
|
||||
logElements.add(new LogElement(NOTIFICATION, true));
|
||||
logElements.add(new LogElement(NSCLIENT, 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.db.ExtendedBolus;
|
||||
import info.nightscout.androidaps.db.TemporaryBasal;
|
||||
import info.nightscout.androidaps.events.EventCustomActionsChanged;
|
||||
import info.nightscout.androidaps.events.EventExtendedBolusChange;
|
||||
import info.nightscout.androidaps.events.EventInitializationChanged;
|
||||
import info.nightscout.androidaps.events.EventRefreshOverview;
|
||||
|
@ -133,6 +134,11 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
|
|||
updateGUI();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onStatusEvent(final EventCustomActionsChanged ev) {
|
||||
updateGUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateGUI() {
|
||||
Activity activity = getActivity();
|
||||
|
@ -247,6 +253,9 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
|
|||
|
||||
for (CustomAction customAction : customActions) {
|
||||
|
||||
if (!customAction.isEnabled())
|
||||
continue;
|
||||
|
||||
SingleClickButton btn = new SingleClickButton(getContext(), null, android.R.attr.buttonStyle);
|
||||
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.pumpCustomButtons.add(btn);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -283,7 +290,6 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
|
|||
}
|
||||
|
||||
pumpCustomButtons.clear();
|
||||
pumpCustomActions.clear();
|
||||
}
|
||||
|
||||
|
||||
|
@ -335,4 +341,4 @@ public class ActionsFragment extends SubscriberFragment implements View.OnClickL
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,31 +12,36 @@ public class CustomAction {
|
|||
private String iconName;
|
||||
private CustomActionType customActionType;
|
||||
private int iconResourceId;
|
||||
private boolean enabled = true;
|
||||
|
||||
|
||||
public CustomAction(int nameResourceId, CustomActionType actionType) {
|
||||
this.name = nameResourceId;
|
||||
this.customActionType = actionType;
|
||||
this.iconResourceId = R.drawable.icon_actions_profileswitch;
|
||||
this(nameResourceId, actionType, R.drawable.icon_actions_profileswitch, true);
|
||||
}
|
||||
|
||||
|
||||
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.customActionType = actionType;
|
||||
this.iconResourceId = iconResourceId;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
public int getName() {
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public CustomActionType getCustomActionType() {
|
||||
|
||||
return customActionType;
|
||||
}
|
||||
|
||||
|
@ -44,4 +49,15 @@ public class CustomAction {
|
|||
public int getIconResourceId() {
|
||||
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 bolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep;
|
||||
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);
|
||||
|
@ -184,7 +184,7 @@ public class FillDialog extends DialogFragment implements OnClickListener {
|
|||
confirmMessage.add("");
|
||||
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)
|
||||
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())
|
||||
|
|
|
@ -42,12 +42,12 @@ public class NewExtendedBolusDialog extends DialogFragment implements View.OnCli
|
|||
|
||||
Double maxInsulin = MainApp.getConstraintChecker().getMaxExtendedBolusAllowed().value();
|
||||
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 extendedMaxDuration = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().extendedBolusMaxDuration;
|
||||
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.cancel).setOnClickListener(this);
|
||||
|
|
|
@ -66,17 +66,17 @@ public class NewTempBasalDialog extends DialogFragment implements View.OnClickLi
|
|||
basalPercent = (NumberPicker) view.findViewById(R.id.overview_newtempbasal_basalpercentinput);
|
||||
double maxTempPercent = pumpDescription.maxTempPercent;
|
||||
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();
|
||||
Double currentBasal = profile != null ? profile.getBasal() : 0d;
|
||||
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 tempMaxDuration = pumpDescription.tempMaxDuration;
|
||||
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) {
|
||||
// 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);
|
||||
editTemptarget = (NumberPicker) view.findViewById(R.id.careportal_newnstreatment_temptarget);
|
||||
if (profile == null) {
|
||||
editBg.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, bgTextWatcher);
|
||||
editTemptarget.setParams(bg, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false);
|
||||
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, view.findViewById(R.id.ok));
|
||||
} else if (units.equals(Constants.MMOL)) {
|
||||
editBg.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false, bgTextWatcher);
|
||||
editTemptarget.setParams(bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false);
|
||||
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, view.findViewById(R.id.ok));
|
||||
} else {
|
||||
editBg.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false, bgTextWatcher);
|
||||
editTemptarget.setParams(bg, 0d, 500d, 1d, new DecimalFormat("0"), false);
|
||||
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, view.findViewById(R.id.ok));
|
||||
}
|
||||
|
||||
sensorRadioButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
|
@ -287,16 +287,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
|
|||
|
||||
Integer maxCarbs = MainApp.getConstraintChecker().getMaxCarbsAllowed().value();
|
||||
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();
|
||||
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.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.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() {
|
||||
@Override
|
||||
|
@ -320,7 +320,7 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
|
|||
if (profile != null)
|
||||
maxPercent = MainApp.getConstraintChecker().getMaxBasalPercentAllowed(profile).value();
|
||||
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() {
|
||||
@Override
|
||||
|
@ -344,16 +344,16 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick
|
|||
if (profile != null)
|
||||
maxAbsolute = MainApp.getConstraintChecker().getMaxBasalAllowed(profile).value();
|
||||
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.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.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.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());
|
||||
if (ps != null && ps.isCPP) {
|
||||
|
|
|
@ -1567,4 +1567,4 @@ public class OverviewFragment extends Fragment implements View.OnClickListener,
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,9 +66,9 @@ public class CalibrationDialog extends DialogFragment implements View.OnClickLis
|
|||
bgNumber = (NumberPicker) view.findViewById(R.id.overview_calibration_bg);
|
||||
|
||||
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
|
||||
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.setText(units);
|
||||
|
|
|
@ -130,15 +130,15 @@ public class NewCarbsDialog extends DialogFragment implements OnClickListener, C
|
|||
startHypoTTCheckbox = view.findViewById(R.id.newcarbs_hypo_tt);
|
||||
|
||||
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.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();
|
||||
|
||||
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);
|
||||
fav1Button.setOnClickListener(this);
|
||||
|
|
|
@ -126,12 +126,12 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener
|
|||
editLayout = view.findViewById(R.id.newinsulin_time_layout);
|
||||
editLayout.setVisibility(View.GONE);
|
||||
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();
|
||||
|
||||
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);
|
||||
plus1Button.setOnClickListener(this);
|
||||
|
@ -226,7 +226,7 @@ public class NewInsulinDialog extends DialogFragment implements OnClickListener
|
|||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
editInsulin = (NumberPicker) view.findViewById(R.id.treatments_newtreatment_insulinamount);
|
||||
|
||||
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false, textWatcher);
|
||||
editInsulin.setParams(0d, 0d, maxInsulin, ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep, DecimalFormatter.pumpSupportedBolusFormat(), 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, view.findViewById(R.id.ok), textWatcher);
|
||||
|
||||
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>";
|
||||
}
|
||||
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)
|
||||
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();
|
||||
Double maxCorrection = MainApp.getConstraintChecker().getMaxBolusAllowed().value();
|
||||
|
||||
editBg.setParams(0d, 0d, 500d, 0.1d, new DecimalFormat("0.0"), false, textWatcher);
|
||||
editCarbs.setParams(0d, 0d, (double) maxCarbs, 1d, new DecimalFormat("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, view.findViewById(R.id.ok), textWatcher);
|
||||
double bolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep;
|
||||
editCorr.setParams(0d, -maxCorrection, maxCorrection, bolusstep, DecimalFormatter.pumpSupportedBolusFormat(), false, textWatcher);
|
||||
editCarbTime.setParams(0d, -60d, 60d, 5d, new DecimalFormat("0"), false);
|
||||
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, view.findViewById(R.id.ok), textWatcher);
|
||||
initDialog();
|
||||
|
||||
setCancelable(true);
|
||||
|
@ -392,7 +392,9 @@ public class WizardDialog extends DialogFragment implements OnClickListener, Com
|
|||
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()));
|
||||
bgInsulin.setText(StringUtils.formatInsulin(wizard.getInsulinFromBG()));
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package info.nightscout.androidaps.plugins.general.overview.notifications;
|
||||
|
||||
import java.util.Date;
|
||||
|
@ -75,6 +74,8 @@ public class Notification {
|
|||
public static final int DST_IN_24H = 50;
|
||||
public static final int DISKFULL = 51;
|
||||
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;
|
||||
|
@ -85,6 +86,7 @@ public class Notification {
|
|||
|
||||
public NSAlarm nsAlarm = null;
|
||||
public Integer soundId = null;
|
||||
|
||||
public Notification() {
|
||||
}
|
||||
|
||||
|
@ -198,10 +200,10 @@ public class Notification {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static boolean isAlarmForStaleData(){
|
||||
public static boolean isAlarmForStaleData() {
|
||||
long snoozedTo = SP.getLong("snoozedTo", 0L);
|
||||
if(snoozedTo != 0L){
|
||||
if(System.currentTimeMillis() < SP.getLong("snoozedTo", 0L)) {
|
||||
if (snoozedTo != 0L) {
|
||||
if (System.currentTimeMillis() < SP.getLong("snoozedTo", 0L)) {
|
||||
//log.debug("Alarm is snoozed for next "+(SP.getLong("snoozedTo", 0L)-System.currentTimeMillis())/1000+" seconds");
|
||||
return false;
|
||||
}
|
||||
|
@ -217,12 +219,12 @@ public class Notification {
|
|||
//log.debug("bgReadingAgoMin value is:"+bgReadingAgoMin);
|
||||
//log.debug("Stale alarm snoozed to: "+(System.currentTimeMillis() - snoozedTo)/60000L);
|
||||
Double threshold = NSSettingsStatus.getInstance().getThreshold("alarmTimeagoWarnMins");
|
||||
//log.debug("OpenAPS Alerts enabled: "+openAPSEnabledAlerts);
|
||||
// if no thresshold from Ns get it loccally
|
||||
if(threshold == null) threshold = SP.getDouble(R.string.key_nsalarm_staledatavalue,15D);
|
||||
// No threshold of OpenAPS Alarm so using the one for BG
|
||||
// Added OpenAPSEnabledAlerts to alarm check
|
||||
if((bgReadingAgoMin > threshold && SP.getBoolean(R.string.key_nsalarm_staledata, false))||(bgReadingAgoMin > threshold && openAPSEnabledAlerts)){
|
||||
//log.debug("OpenAPS Alerts enabled: "+openAPSEnabledAlerts);
|
||||
// if no thresshold from Ns get it loccally
|
||||
if (threshold == null) threshold = SP.getDouble(R.string.key_nsalarm_staledatavalue, 15D);
|
||||
// No threshold of OpenAPS Alarm so using the one for BG
|
||||
// Added OpenAPSEnabledAlerts to alarm check
|
||||
if ((bgReadingAgoMin > threshold && SP.getBoolean(R.string.key_nsalarm_staledata, false)) || (bgReadingAgoMin > threshold && openAPSEnabledAlerts)) {
|
||||
return true;
|
||||
}
|
||||
//snoozing for threshold
|
||||
|
|
|
@ -790,7 +790,7 @@ public class SmsCommunicatorPlugin extends PluginBase {
|
|||
passCode += Character.toString((char) (startChar2 + Math.random() * ('z' - 'a' + 1)));
|
||||
int startChar3 = Math.random() > 0.5 ? 'a' : 'A';
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -518,6 +518,20 @@ public class IobCobCalculatorPlugin extends PluginBase {
|
|||
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
|
||||
public AutosensData getLastAutosensData(String reason) {
|
||||
if (autosensDataTable.size() < 1) {
|
||||
|
@ -801,4 +815,4 @@ public class IobCobCalculatorPlugin extends PluginBase {
|
|||
if (upper >= arr.length) return arr[(int) lower];
|
||||
return arr[(int) lower] * (1 - weight) + arr[(int) upper] * weight;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -78,7 +78,7 @@ public class LocalProfileFragment extends SubscriberFragment {
|
|||
PumpDescription pumpDescription = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription();
|
||||
View layout = inflater.inflate(R.layout.localprofile_fragment, container, false);
|
||||
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);
|
||||
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);
|
||||
|
@ -124,7 +124,7 @@ public class LocalProfileFragment extends SubscriberFragment {
|
|||
LocalProfilePlugin.getPlugin().loadSettings();
|
||||
mgdlView.setChecked(LocalProfilePlugin.getPlugin().mgdl);
|
||||
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);
|
||||
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);
|
||||
|
|
|
@ -39,6 +39,7 @@ import info.nightscout.androidaps.interfaces.PluginType;
|
|||
import info.nightscout.androidaps.interfaces.PumpDescription;
|
||||
import info.nightscout.androidaps.interfaces.PumpInterface;
|
||||
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.ConfigBuilderPlugin;
|
||||
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions;
|
||||
|
@ -163,33 +164,10 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint
|
|||
}
|
||||
|
||||
@Override
|
||||
public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity context) {
|
||||
boolean allowHardwarePump = SP.getBoolean("allow_hardware_pump", false);
|
||||
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();
|
||||
}
|
||||
public void switchAllowed(ConfigBuilderFragment.PluginViewHolder.PluginSwitcher pluginSwitcher, FragmentActivity activity) {
|
||||
confirmPumpPluginActivation(pluginSwitcher, activity);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return pump.initialized;
|
||||
|
@ -1313,13 +1291,13 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint
|
|||
}
|
||||
|
||||
@Override
|
||||
public String manufacter() {
|
||||
return "Roche";
|
||||
public ManufacturerType manufacturer() {
|
||||
return ManufacturerType.Roche;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String model() {
|
||||
return "Combo";
|
||||
public PumpType model() {
|
||||
return PumpType.AccuChekCombo;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1407,4 +1385,10 @@ public class ComboPlugin extends PluginBase implements PumpInterface, Constraint
|
|||
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;
|
||||
|
||||
}
|
|
@ -91,4 +91,4 @@ public enum DoseStepSize
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public enum PumpCapability {
|
|||
DanaCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, TDD, ManualTDDLoad), //
|
||||
DanaWithHistoryCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill, StoreCarbInfo, TDD, ManualTDDLoad), //
|
||||
InsightCapabilities(Bolus, ExtendedBolus, TempBasal, BasalProfileSet, Refill,TDD,BasalRate30min), //
|
||||
|
||||
MedtronicCapabilities(Bolus, TempBasal, BasalProfileSet, Refill, TDD), //
|
||||
|
||||
// BasalRates (separately grouped)
|
||||
BasalRate_Duration15minAllowed, //
|
||||
|
@ -69,4 +69,4 @@ public enum PumpCapability {
|
|||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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.R;
|
||||
import info.nightscout.androidaps.plugins.common.ManufacturerType;
|
||||
import info.nightscout.androidaps.plugins.pump.common.data.DoseSettings;
|
||||
import info.nightscout.androidaps.utils.Round;
|
||||
|
||||
|
@ -18,7 +19,7 @@ import info.nightscout.androidaps.utils.Round;
|
|||
|
||||
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), //
|
||||
PumpTempBasalType.Percent, //
|
||||
new DoseSettings(10, 30, 24 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||
|
@ -26,7 +27,7 @@ public enum PumpType {
|
|||
|
||||
// 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),
|
||||
PumpTempBasalType.Percent,
|
||||
new DoseSettings(5, 30, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||
|
@ -34,32 +35,32 @@ public enum PumpType {
|
|||
|
||||
// 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), //
|
||||
PumpTempBasalType.Percent,
|
||||
new DoseSettings(10, 15, 12 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||
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), //
|
||||
PumpTempBasalType.Percent,
|
||||
new DoseSettings(10, 15, 12 * 60, 0d, 500d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||
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), //
|
||||
PumpTempBasalType.Percent,
|
||||
new DoseSettings(10, 15, 24 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||
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), //
|
||||
PumpTempBasalType.Percent,
|
||||
new DoseSettings(10, 15, 24 * 60, 0d, 250d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||
0.02d, 0.01d, DoseStepSize.InsightBolus, PumpCapability.InsightCapabilities), //
|
||||
|
||||
// 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), //
|
||||
PumpTempBasalType.Percent, //
|
||||
new DoseSettings(10, 30, 24 * 60, 0d, 300d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||
|
@ -68,19 +69,19 @@ public enum PumpType {
|
|||
AnimasPing("Animas Ping", "Ping", AnimasVibe),
|
||||
|
||||
// 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), //
|
||||
PumpTempBasalType.Percent, //
|
||||
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, //
|
||||
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), //
|
||||
PumpTempBasalType.Percent, //
|
||||
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minNotAllowed, //
|
||||
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), //
|
||||
PumpTempBasalType.Percent, //
|
||||
new DoseSettings(10d, 60, 24 * 60, 0d, 200d), PumpCapability.BasalRate_Duration15and30minAllowed, //
|
||||
|
@ -90,38 +91,38 @@ public enum PumpType {
|
|||
|
||||
|
||||
// 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), //
|
||||
PumpTempBasalType.Absolute, //
|
||||
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),
|
||||
|
||||
// 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), //
|
||||
PumpTempBasalType.Absolute, //
|
||||
new DoseSettings(0.05d, 30, 24 * 60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||
0.05d, 0.05d, null, PumpCapability.VirtualPumpCapabilities), // TODO
|
||||
new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||
0.05d, 0.05d, null, PumpCapability.MedtronicCapabilities), //
|
||||
|
||||
Medtronic_515_715("Medtronic 515/715", "515/715", 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), //
|
||||
PumpTempBasalType.Absolute, //
|
||||
new DoseSettings(0.05d, 30, 24 * 60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||
0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPumpCapabilities), //
|
||||
new DoseSettings(0.05d, 30, 24*60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||
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_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), //
|
||||
PumpTempBasalType.Absolute, //
|
||||
new DoseSettings(0.05d, 30, 24 * 60, 0d, 35d), PumpCapability.BasalRate_Duration30minAllowed, //
|
||||
0.025d, 0.025d, DoseStepSize.MedtronicVeoBasal, PumpCapability.VirtualPumpCapabilities), //
|
||||
|
||||
// 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),
|
||||
PumpTempBasalType.Percent,
|
||||
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), //
|
||||
TandemTSlimG4("Tandem t:slim G4", "t:slim G4", TandemTSlim), //
|
||||
TandemTSlimX2("Tandem t:slim X2", "t:slim X2", TandemTSlim), //
|
||||
|
||||
// MDI
|
||||
MDI("MDI", ManufacturerType.AndroidAPS, "MDI")
|
||||
;
|
||||
|
||||
|
||||
private String description;
|
||||
private String manufacter;
|
||||
private ManufacturerType manufacturer;
|
||||
private String model;
|
||||
private double bolusSize;
|
||||
private DoseStepSize specialBolusSize;
|
||||
|
@ -163,9 +168,18 @@ public enum PumpType {
|
|||
{
|
||||
this.description = description;
|
||||
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)
|
||||
{
|
||||
this.description = description;
|
||||
|
@ -174,20 +188,20 @@ public enum PumpType {
|
|||
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, //
|
||||
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, //
|
||||
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, //
|
||||
PumpTempBasalType pumpTempBasalType, DoseSettings tbrSettings, PumpCapability specialBasalDurations, //
|
||||
double baseBasalMinValue, Double baseBasalMaxValue, double baseBasalStep, DoseStepSize baseBasalSpecialSteps, PumpCapability pumpCapability) {
|
||||
this.description = description;
|
||||
this.manufacter = manufacter;
|
||||
this.manufacturer = manufacturer;
|
||||
this.model = model;
|
||||
this.bolusSize = bolusSize;
|
||||
this.specialBolusSize = specialBolusSize;
|
||||
|
@ -207,8 +221,8 @@ public enum PumpType {
|
|||
return description;
|
||||
}
|
||||
|
||||
public String getManufacter() {
|
||||
return isParentSet() ? parent.manufacter : manufacter;
|
||||
public ManufacturerType getManufacturer() {
|
||||
return isParentSet() ? parent.manufacturer : manufacturer;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue