1) Added SMS commands:

- SMS DISABLE/STOP to disable SMS Service;
- TARGET MEAL/ACTIVITY/HYPO to switch to the standard temp targets;
- BOLUS 0.60 MEAL, to do a bolus and set the stadard meal TT (ie 90 mg/dL for 45').

2) Modified the SMS Timeout in a range from 3' to 60'.
This preference is useful for parents that will manage many boluses in a single meal (because often, eaten foods are unpredictable with babies, so parents need to perform many closed boluses).
Due to safety reasons (prevent multiple boluses for stolen phones, cloned SIM, ...):
- Added a further SMS command to disable SMS Services (SMS DISABLE/STOP);
- To modify the default timeout of 15', 2 Phone Numbers are mandatory
In this way, if unwanted/suspicious boluses are notified, the other parent can disable Remote Management (till to re-enable it directly with the AAPS in the master smartphone).
This commit is contained in:
fabriziocasellato 2019-11-07 10:39:17 +01:00
parent ff70299625
commit 86d4b755ea
4 changed files with 234 additions and 5 deletions

View file

@ -13,6 +13,7 @@ import android.preference.PreferenceScreen;
import android.text.TextUtils;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.bus.RxBus;
@ -51,6 +52,8 @@ import info.nightscout.androidaps.utils.OKDialog;
import info.nightscout.androidaps.utils.SP;
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin;
import com.andreabaccega.widget.ValidatingEditTextPreference;
public class PreferencesActivity extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
MyPreferenceFragment myPreferenceFragment;
@ -218,6 +221,38 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre
TidepoolUploader.INSTANCE.testLogin(getActivity());
return false;
});
final ValidatingEditTextPreference distance = (ValidatingEditTextPreference)findPreference(getString(R.string.key_smscommunicator_remotebolusmindistance));
final EditTextPreference allowedNumbers = (EditTextPreference)findPreference(getString(R.string.key_smscommunicator_allowednumbers));
if (distance != null && allowedNumbers != null) {
if (!SmsCommunicatorPlugin.areMoreNumbers(allowedNumbers.getText())) {
distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance)
+ ".\n"
+ getString(R.string.smscommunicator_remotebolusmindistance_caveat));
distance.setEnabled(false);
} else {
distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance));
distance.setEnabled(true);
}
allowedNumbers.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (!SmsCommunicatorPlugin.areMoreNumbers(((String)newValue))) {
distance.setText(String.valueOf(Constants.remoteBolusMinDistance/(60 * 1000L)));
distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance)
+ ".\n"
+ getString(R.string.smscommunicator_remotebolusmindistance_caveat));
distance.setEnabled(false);
} else {
distance.setTitle(getString(R.string.smscommunicator_remotebolusmindistance));
distance.setEnabled(true);
}
return true;
}
});
}
}
@Override

View file

@ -23,6 +23,7 @@ import info.nightscout.androidaps.data.ProfileStore;
import info.nightscout.androidaps.db.BgReading;
import info.nightscout.androidaps.db.DatabaseHelper;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.events.EventPreferenceChange;
import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.interfaces.Constraint;
@ -136,6 +137,8 @@ public class SmsCommunicatorPlugin extends PluginBase {
case "EXTENDED":
case "CAL":
case "PROFILE":
case "TARGET":
case "SMS":
return true;
}
if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(number))
@ -242,7 +245,7 @@ public class SmsCommunicatorPlugin extends PluginBase {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotebolusnotallowed));
else if (splitted.length == 2 && ConfigBuilderPlugin.getPlugin().getActivePump().isSuspended())
sendSMS(new Sms(receivedSms.phoneNumber, R.string.pumpsuspended));
else if (splitted.length == 2)
else if (splitted.length == 2 || splitted.length == 3)
processBOLUS(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
@ -255,6 +258,22 @@ public class SmsCommunicatorPlugin extends PluginBase {
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "TARGET":
if (!remoteCommandsAllowed)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed));
else if (splitted.length == 2)
processTARGET(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
case "SMS":
if (!remoteCommandsAllowed)
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_remotecommandnotallowed));
else if (splitted.length == 2)
processSMS(splitted, receivedSms);
else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
break;
default: // expect passCode here
if (messageToConfirm != null && messageToConfirm.requester.phoneNumber.equals(receivedSms.phoneNumber)) {
messageToConfirm.action(splitted[0]);
@ -680,10 +699,19 @@ public class SmsCommunicatorPlugin extends PluginBase {
private void processBOLUS(String[] splitted, Sms receivedSms) {
Double bolus = SafeParse.stringToDouble(splitted[1]);
final boolean isMeal = splitted.length > 2 && splitted[2].equalsIgnoreCase("MEAL");
bolus = MainApp.getConstraintChecker().applyBolusConstraints(new Constraint<>(bolus)).value();
if (bolus > 0d) {
if (splitted.length == 3 && !isMeal) {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
} else if (bolus > 0d) {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode);
String reply = "";
if (isMeal) {
reply = String.format(MainApp.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode);
} else {
reply = String.format(MainApp.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode);
}
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction(bolus) {
@Override
@ -701,10 +729,40 @@ public class SmsCommunicatorPlugin extends PluginBase {
public void run() {
PumpInterface pump = ConfigBuilderPlugin.getPlugin().getActivePump();
if (resultSuccess) {
String reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered);
String reply = "";
if (isMeal) {
reply = String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered);
} else {
reply = String.format(MainApp.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered);
}
if (pump != null)
reply += "\n" + pump.shortStatus(true);
lastRemoteBolusTime = DateUtil.now();
if (isMeal) {
Profile currentProfile = ProfileFunctions.getInstance().getProfile();
int eatingSoonTTDuration = SP.getInt(R.string.key_eatingsoon_duration, Constants.defaultEatingSoonTTDuration);
eatingSoonTTDuration = eatingSoonTTDuration > 0 ? eatingSoonTTDuration : Constants.defaultEatingSoonTTDuration;
double eatingSoonTT = SP.getDouble(R.string.key_eatingsoon_target, currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl);
eatingSoonTT = eatingSoonTT > 0 ? eatingSoonTT : currentProfile.getUnits().equals(Constants.MMOL) ? Constants.defaultEatingSoonTTmmol : Constants.defaultEatingSoonTTmgdl;
TempTarget tempTarget = new TempTarget()
.date(System.currentTimeMillis())
.duration(eatingSoonTTDuration)
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(eatingSoonTT, currentProfile.getUnits()))
.high(Profile.toMgdl(eatingSoonTT, currentProfile.getUnits()));
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
String tt = "";
if (currentProfile.getUnits().equals(Constants.MMOL)) {
tt = DecimalFormatter.to1Decimal(eatingSoonTT);
} else
tt = DecimalFormatter.to0Decimal(eatingSoonTT);
reply += String.format(MainApp.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration);
}
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} else {
String reply = MainApp.gs(R.string.smscommunicator_bolusfailed);
@ -722,6 +780,105 @@ public class SmsCommunicatorPlugin extends PluginBase {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
}
private void processTARGET(String[] splitted, Sms receivedSms) {
boolean isMeal = splitted[1].equalsIgnoreCase("MEAL");
boolean isActivity = splitted[1].equalsIgnoreCase("ACTIVITY");
boolean isHypo = splitted[1].equalsIgnoreCase("HYPO");
if (isMeal || isActivity || isHypo) {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_temptargetwithcode), splitted[1].toUpperCase(), passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() {
@Override
public void run() {
try {
Profile currentProfile = ProfileFunctions.getInstance().getProfile();
int keyDuration = 0;
Integer defaultTargetDuration = 0;
int keyTarget = 0;
double defaultTargetMMOL = 0d;
double defaultTargetMGDL = 0d;
if (isMeal) {
keyDuration = R.string.key_eatingsoon_duration;
defaultTargetDuration = Constants.defaultEatingSoonTTDuration;
keyTarget = R.string.key_eatingsoon_target;
defaultTargetMMOL = Constants.defaultEatingSoonTTmmol;
defaultTargetMGDL = Constants.defaultEatingSoonTTmgdl;
} else if (isActivity) {
keyDuration = R.string.key_activity_duration;
defaultTargetDuration = Constants.defaultActivityTTDuration;
keyTarget = R.string.key_activity_target;
defaultTargetMMOL = Constants.defaultActivityTTmmol;
defaultTargetMGDL = Constants.defaultActivityTTmgdl;
} else if (isHypo) {
keyDuration = R.string.key_hypo_duration;
defaultTargetDuration = Constants.defaultHypoTTDuration;
keyTarget = R.string.key_hypo_target;
defaultTargetMMOL = Constants.defaultHypoTTmmol;
defaultTargetMGDL = Constants.defaultHypoTTmgdl;
}
int ttDuration = SP.getInt(keyDuration, defaultTargetDuration);
ttDuration = ttDuration > 0 ? ttDuration : defaultTargetDuration;
double tt = SP.getDouble(keyTarget, currentProfile.getUnits().equals(Constants.MMOL) ? defaultTargetMMOL : defaultTargetMGDL);
tt = tt > 0 ? tt : currentProfile.getUnits().equals(Constants.MMOL) ? defaultTargetMMOL : defaultTargetMGDL;
TempTarget tempTarget = new TempTarget()
.date(System.currentTimeMillis())
.duration(ttDuration)
.reason(MainApp.gs(R.string.eatingsoon))
.source(Source.USER)
.low(Profile.toMgdl(tt, currentProfile.getUnits()))
.high(Profile.toMgdl(tt, currentProfile.getUnits()));
TreatmentsPlugin.getPlugin().addToHistoryTempTarget(tempTarget);
String ttString = "";
if (currentProfile.getUnits().equals(Constants.MMOL))
ttString = DecimalFormatter.to1Decimal(tt);
else
ttString = DecimalFormatter.to0Decimal(tt);
String reply = String.format(MainApp.gs(R.string.smscommunicator_tt_set), ttString, ttDuration);
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} catch (Exception e) {
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand));
return;
}
}
});
} else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
}
private void processSMS(String[] splitted, Sms receivedSms) {
boolean isStop = splitted[1].equalsIgnoreCase("STOP")
|| splitted[1].equalsIgnoreCase("DISABLE");
if (isStop) {
String passCode = generatePasscode();
String reply = String.format(MainApp.gs(R.string.smscommunicator_stopsmswithcode), passCode);
receivedSms.processed = true;
messageToConfirm = new AuthRequest(this, receivedSms, reply, passCode, new SmsAction() {
@Override
public void run() {
try {
SP.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false);
String reply = String.format(MainApp.gs(R.string.smscommunicator_stoppedsms));
sendSMSToAllNumbers(new Sms(receivedSms.phoneNumber, reply));
} catch (Exception e) {
e.printStackTrace();
sendSMS(new Sms(receivedSms.phoneNumber, R.string.smscommunicator_unknowncommand));
return;
}
}
});
} else
sendSMS(new Sms(receivedSms.phoneNumber, R.string.wrongformat));
}
private void processCAL(String[] splitted, Sms receivedSms) {
Double cal = SafeParse.stringToDouble(splitted[1]);
if (cal > 0d) {
@ -809,4 +966,19 @@ public class SmsCommunicatorPlugin extends PluginBase {
s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
return s;
}
public static boolean areMoreNumbers(String allowednumbers) {
int countNumbers = 0;
String[] substrings = allowednumbers.split(";");
for (String number : substrings) {
String cleaned = number.replaceAll("\\s+", "");
if (cleaned.length()<4) continue;
if (cleaned.substring(0,1).compareTo("+")!=0) continue;
cleaned = cleaned.replace("+","");
if (!cleaned.matches("[0-9]+")) continue;
countNumbers++;
}
return countNumbers > 1;
}
}

View file

@ -292,11 +292,22 @@
<string name="smscommunicator_allowednumbers">Allowed phone numbers</string>
<string name="smscommunicator_allowednumbers_summary">+XXXXXXXXXX;+YYYYYYYYYY</string>
<string name="smscommunicator_bolusreplywithcode">To deliver bolus %1$.2fU reply with code %2$s</string>
<string name="smscommunicator_mealbolusreplywithcode">To deliver meal bolus %1$.2fU reply with code %2$s</string>
<string name="smscommunicator_temptargetwithcode">To set the Temp Target %1$s reply with code %2$s</string>
<string name="smscommunicator_stopsmswithcode">To disable the SMS Remote Service reply with code %1$s.\n\nKeep in mind that you\'ll able to reactivate it directly from the AAPS master smartphone only.</string>
<string name="smscommunicator_stoppedsms">SMS Remote Service stopped. To reactivate it, use AAPS on master smartphone.</string>
<string name="smscommunicator_calibrationreplywithcode">To send calibration %1$.2f reply with code %2$s</string>
<string name="smscommunicator_bolusfailed">Bolus failed</string>
<string name="key_smscommunicator_remotebolusmindistance" translatable="false">smscommunicator_remotebolusmindistance</string>
<string name="smscommunicator_remotebolusmindistance_summary">Minimum number of minutes that must elapse between one remote bolus and the next</string>
<string name="smscommunicator_remotebolusmindistance">How many minutes must elapse, at least, between one bolus and the next</string>
<string name="smscommunicator_remotebolusmindistance_caveat">For your safety, to edit this preference you need to add at least 2 phone numbers.</string>
<string name="bolusdelivered">Bolus %1$.2fU delivered successfully</string>
<string name="bolusrequested">Going to deliver %1$.2fU</string>
<string name="smscommunicator_bolusdelivered">Bolus %1$.2fU delivered successfully</string>
<string name="smscommunicator_mealbolusdelivered">Meal Bolus %1$.2fU delivered successfully</string>
<string name="smscommunicator_mealbolusdelivered_tt">Target %1$s for %2$d minutes</string>
<string name="smscommunicator_tt_set">Target %1$s for %2$d minutes set succesfully</string>
<string name="bolusdelivering">Delivering %1$.2fU</string>
<string name="smscommunicator_remotecommandsallowed">Allow remote commands via SMS</string>
<string name="glucosetype_finger">Finger</string>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:validate="http://schemas.android.com/apk/res-auto">
<PreferenceCategory
android:key="smscommunicator"
android:title="@string/smscommunicator">
@ -8,6 +9,16 @@
android:key="@string/key_smscommunicator_allowednumbers"
android:summary="@string/smscommunicator_allowednumbers_summary"
android:title="@string/smscommunicator_allowednumbers" />
<com.andreabaccega.widget.ValidatingEditTextPreference
android:key="@string/key_smscommunicator_remotebolusmindistance"
android:defaultValue="15"
android:summary="@string/smscommunicator_remotebolusmindistance_summary"
android:title="@string/smscommunicator_remotebolusmindistance"
validate:minNumber="3"
validate:maxNumber="60"
validate:testType="numericRange"
android:singleLineTitle="false"
/>
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_smscommunicator_remotecommandsallowed"