NSClientService -> kt
This commit is contained in:
parent
840de7b3b9
commit
07b3557b81
3 changed files with 751 additions and 840 deletions
|
@ -22,6 +22,7 @@ class NsClientReceiverDelegate @Inject constructor(
|
|||
private var allowedChargingState = true
|
||||
private var allowedNetworkState = true
|
||||
var allowed = true
|
||||
|
||||
fun grabReceiversState() {
|
||||
receiverStatusStore.updateNetworkStatus()
|
||||
}
|
||||
|
|
|
@ -1,840 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.nsclient.services;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.hash.Hashing;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.DaggerService;
|
||||
import dagger.android.HasAndroidInjector;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.database.AppRepository;
|
||||
import info.nightscout.androidaps.events.EventAppExit;
|
||||
import info.nightscout.androidaps.events.EventConfigBuilderChange;
|
||||
import info.nightscout.androidaps.events.EventPreferenceChange;
|
||||
import info.nightscout.androidaps.interfaces.Config;
|
||||
import info.nightscout.androidaps.interfaces.DataSyncSelector;
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
|
||||
import info.nightscout.androidaps.interfaces.PluginType;
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
|
||||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddAckWorker;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientAddUpdateWorker;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientMbgWorker;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientRemoveWorker;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.NSClientUpdateRemoveAckWorker;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAuthAck;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus;
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI;
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction;
|
||||
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin;
|
||||
import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin;
|
||||
import info.nightscout.androidaps.receivers.DataWorker;
|
||||
import info.nightscout.androidaps.services.Intents;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy;
|
||||
import info.nightscout.androidaps.utils.JsonHelper;
|
||||
import info.nightscout.androidaps.utils.T;
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers;
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.socket.client.IO;
|
||||
import io.socket.client.Socket;
|
||||
import io.socket.emitter.Emitter;
|
||||
|
||||
public class NSClientService extends DaggerService {
|
||||
@Inject HasAndroidInjector injector;
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject AapsSchedulers aapsSchedulers;
|
||||
@Inject NSSettingsStatus nsSettingsStatus;
|
||||
@Inject NSDeviceStatus nsDeviceStatus;
|
||||
@Inject DatabaseHelperInterface databaseHelper;
|
||||
@Inject RxBusWrapper rxBus;
|
||||
@Inject ResourceHelper resourceHelper;
|
||||
@Inject SP sp;
|
||||
@Inject FabricPrivacy fabricPrivacy;
|
||||
@Inject NSClientPlugin nsClientPlugin;
|
||||
@Inject BuildHelper buildHelper;
|
||||
@Inject Config config;
|
||||
@Inject DateUtil dateUtil;
|
||||
@Inject DataWorker dataWorker;
|
||||
@Inject DataSyncSelector dataSyncSelector;
|
||||
@Inject AppRepository repository;
|
||||
|
||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||
|
||||
// public PowerManager.WakeLock mWakeLock;
|
||||
private final IBinder mBinder = new NSClientService.LocalBinder();
|
||||
|
||||
private Handler handler;
|
||||
|
||||
public Socket mSocket;
|
||||
public boolean isConnected = false;
|
||||
public boolean hasWriteAuth = false;
|
||||
private Integer dataCounter = 0;
|
||||
private Integer connectCounter = 0;
|
||||
|
||||
|
||||
private boolean nsEnabled = false;
|
||||
public String nsURL = "";
|
||||
private String nsAPISecret = "";
|
||||
private String nsDevice = "";
|
||||
private final Integer nsHours = 48;
|
||||
|
||||
public long lastAckTime = 0;
|
||||
|
||||
public long latestDateInReceivedData = 0;
|
||||
|
||||
private String nsAPIhashCode = "";
|
||||
|
||||
private final ArrayList<Long> reconnections = new ArrayList<>();
|
||||
private final int WATCHDOG_INTERVAL_MINUTES = 2;
|
||||
private final int WATCHDOG_RECONNECT_IN = 15;
|
||||
private final int WATCHDOG_MAX_CONNECTIONS = 5;
|
||||
|
||||
public NSClientService() {
|
||||
super();
|
||||
if (handler == null) {
|
||||
HandlerThread handlerThread = new HandlerThread(NSClientService.class.getSimpleName() + "Handler");
|
||||
handlerThread.start();
|
||||
handler = new Handler(handlerThread.getLooper());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
// mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService");
|
||||
// mWakeLock.acquire();
|
||||
|
||||
initialize();
|
||||
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventConfigBuilderChange.class)
|
||||
.observeOn(aapsSchedulers.getIo())
|
||||
.subscribe(event -> {
|
||||
if (nsEnabled != nsClientPlugin.isEnabled(PluginType.GENERAL)) {
|
||||
latestDateInReceivedData = 0;
|
||||
destroy();
|
||||
initialize();
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
);
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventPreferenceChange.class)
|
||||
.observeOn(aapsSchedulers.getIo())
|
||||
.subscribe(event -> {
|
||||
if (event.isChanged(resourceHelper, R.string.key_nsclientinternal_url) ||
|
||||
event.isChanged(resourceHelper, R.string.key_nsclientinternal_api_secret) ||
|
||||
event.isChanged(resourceHelper, R.string.key_nsclientinternal_paused)
|
||||
) {
|
||||
latestDateInReceivedData = 0;
|
||||
destroy();
|
||||
initialize();
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
);
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventAppExit.class)
|
||||
.observeOn(aapsSchedulers.getIo())
|
||||
.subscribe(event -> {
|
||||
aapsLogger.debug(LTag.NSCLIENT, "EventAppExit received");
|
||||
destroy();
|
||||
stopSelf();
|
||||
}, fabricPrivacy::logException)
|
||||
);
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventNSClientRestart.class)
|
||||
.observeOn(aapsSchedulers.getIo())
|
||||
.subscribe(event -> {
|
||||
latestDateInReceivedData = 0;
|
||||
restart();
|
||||
}, fabricPrivacy::logException)
|
||||
);
|
||||
disposable.add(rxBus
|
||||
.toObservable(NSAuthAck.class)
|
||||
.observeOn(aapsSchedulers.getIo())
|
||||
.subscribe(this::processAuthAck, fabricPrivacy::logException)
|
||||
);
|
||||
disposable.add(rxBus
|
||||
.toObservable(NSUpdateAck.class)
|
||||
.observeOn(aapsSchedulers.getIo())
|
||||
.subscribe(this::processUpdateAck, fabricPrivacy::logException)
|
||||
);
|
||||
disposable.add(rxBus
|
||||
.toObservable(NSAddAck.class)
|
||||
.observeOn(aapsSchedulers.getIo())
|
||||
.subscribe(this::processAddAck, fabricPrivacy::logException)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
disposable.clear();
|
||||
// if (mWakeLock.isHeld()) mWakeLock.release();
|
||||
}
|
||||
|
||||
public void processAddAck(NSAddAck ack) {
|
||||
lastAckTime = dateUtil.now();
|
||||
dataWorker.enqueue(
|
||||
new OneTimeWorkRequest.Builder(NSClientAddAckWorker.class)
|
||||
.setInputData(dataWorker.storeInputData(ack, null))
|
||||
.build());
|
||||
}
|
||||
|
||||
public void processUpdateAck(NSUpdateAck ack) {
|
||||
lastAckTime = dateUtil.now();
|
||||
dataWorker.enqueue(
|
||||
new OneTimeWorkRequest.Builder(NSClientUpdateRemoveAckWorker.class)
|
||||
.setInputData(dataWorker.storeInputData(ack, null))
|
||||
.build());
|
||||
}
|
||||
|
||||
public void processAuthAck(NSAuthAck ack) {
|
||||
String connectionStatus = "Authenticated (";
|
||||
if (ack.read) connectionStatus += "R";
|
||||
if (ack.write) connectionStatus += "W";
|
||||
if (ack.write_treatment) connectionStatus += "T";
|
||||
connectionStatus += ')';
|
||||
isConnected = true;
|
||||
hasWriteAuth = ack.write && ack.write_treatment;
|
||||
rxBus.send(new EventNSClientStatus(connectionStatus));
|
||||
rxBus.send(new EventNSClientNewLog("AUTH", connectionStatus));
|
||||
if (!ack.write) {
|
||||
rxBus.send(new EventNSClientNewLog("ERROR", "Write permission not granted !!!!"));
|
||||
}
|
||||
if (!ack.write_treatment) {
|
||||
rxBus.send(new EventNSClientNewLog("ERROR", "Write treatment permission not granted !!!!"));
|
||||
}
|
||||
if (!hasWriteAuth) {
|
||||
Notification noperm = new Notification(Notification.NSCLIENT_NO_WRITE_PERMISSION, resourceHelper.gs(R.string.nowritepermission), Notification.URGENT);
|
||||
rxBus.send(new EventNewNotification(noperm));
|
||||
} else {
|
||||
rxBus.send(new EventDismissNotification(Notification.NSCLIENT_NO_WRITE_PERMISSION));
|
||||
}
|
||||
}
|
||||
|
||||
public class LocalBinder extends Binder {
|
||||
public NSClientService getServiceInstance() {
|
||||
return NSClientService.this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void initialize() {
|
||||
dataCounter = 0;
|
||||
|
||||
readPreferences();
|
||||
|
||||
if (!nsAPISecret.equals(""))
|
||||
//noinspection UnstableApiUsage
|
||||
nsAPIhashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString();
|
||||
|
||||
rxBus.send(new EventNSClientStatus("Initializing"));
|
||||
if (!nsClientPlugin.isAllowed()) {
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "not allowed"));
|
||||
rxBus.send(new EventNSClientStatus("Not allowed"));
|
||||
} else if (nsClientPlugin.getPaused()) {
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "paused"));
|
||||
rxBus.send(new EventNSClientStatus("Paused"));
|
||||
} else if (!nsEnabled) {
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "disabled"));
|
||||
rxBus.send(new EventNSClientStatus("Disabled"));
|
||||
} else if (!nsURL.equals("") && (buildHelper.isEngineeringMode() || nsURL.toLowerCase().startsWith("https://"))) {
|
||||
try {
|
||||
rxBus.send(new EventNSClientStatus("Connecting ..."));
|
||||
IO.Options opt = new IO.Options();
|
||||
opt.forceNew = true;
|
||||
opt.reconnection = true;
|
||||
mSocket = IO.socket(nsURL, opt);
|
||||
mSocket.on(Socket.EVENT_CONNECT, onConnect);
|
||||
mSocket.on(Socket.EVENT_DISCONNECT, onDisconnect);
|
||||
mSocket.on(Socket.EVENT_ERROR, onError);
|
||||
mSocket.on(Socket.EVENT_CONNECT_ERROR, onError);
|
||||
mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onError);
|
||||
mSocket.on(Socket.EVENT_PING, onPing);
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "do connect"));
|
||||
mSocket.connect();
|
||||
mSocket.on("dataUpdate", onDataUpdate);
|
||||
mSocket.on("announcement", onAnnouncement);
|
||||
mSocket.on("alarm", onAlarm);
|
||||
mSocket.on("urgent_alarm", onUrgentAlarm);
|
||||
mSocket.on("clear_alarm", onClearAlarm);
|
||||
} catch (URISyntaxException | RuntimeException e) {
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"));
|
||||
rxBus.send(new EventNSClientStatus("Wrong URL syntax"));
|
||||
}
|
||||
} else if (nsURL.toLowerCase().startsWith("http://")) {
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "NS URL not encrypted"));
|
||||
rxBus.send(new EventNSClientStatus("Not encrypted"));
|
||||
} else {
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "No NS URL specified"));
|
||||
rxBus.send(new EventNSClientStatus("Not configured"));
|
||||
}
|
||||
}
|
||||
|
||||
private final Emitter.Listener onConnect = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
connectCounter++;
|
||||
String socketId = mSocket != null ? mSocket.id() : "NULL";
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "connect #" + connectCounter + " event. ID: " + socketId));
|
||||
if (mSocket != null)
|
||||
sendAuthMessage(new NSAuthAck(rxBus));
|
||||
watchdog();
|
||||
}
|
||||
};
|
||||
|
||||
void watchdog() {
|
||||
synchronized (reconnections) {
|
||||
long now = dateUtil.now();
|
||||
reconnections.add(now);
|
||||
for (int i = 0; i < reconnections.size(); i++) {
|
||||
Long r = reconnections.get(i);
|
||||
if (r < now - T.mins(WATCHDOG_INTERVAL_MINUTES).msecs()) {
|
||||
reconnections.remove(r);
|
||||
}
|
||||
}
|
||||
rxBus.send(new EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " mins: " + reconnections.size() + "/" + WATCHDOG_MAX_CONNECTIONS));
|
||||
if (reconnections.size() >= WATCHDOG_MAX_CONNECTIONS) {
|
||||
Notification n = new Notification(Notification.NS_MALFUNCTION, resourceHelper.gs(R.string.nsmalfunction), Notification.URGENT);
|
||||
rxBus.send(new EventNewNotification(n));
|
||||
rxBus.send(new EventNSClientNewLog("WATCHDOG", "pausing for " + WATCHDOG_RECONNECT_IN + " mins"));
|
||||
nsClientPlugin.pause(true);
|
||||
rxBus.send(new EventNSClientUpdateGUI());
|
||||
new Thread(() -> {
|
||||
SystemClock.sleep(T.mins(WATCHDOG_RECONNECT_IN).msecs());
|
||||
rxBus.send(new EventNSClientNewLog("WATCHDOG", "reenabling NSClient"));
|
||||
nsClientPlugin.pause(false);
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Emitter.Listener onDisconnect = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(Object... args) {
|
||||
aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: {}", args);
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "disconnect event"));
|
||||
}
|
||||
};
|
||||
|
||||
public synchronized void destroy() {
|
||||
if (mSocket != null) {
|
||||
mSocket.off(Socket.EVENT_CONNECT);
|
||||
mSocket.off(Socket.EVENT_DISCONNECT);
|
||||
mSocket.off(Socket.EVENT_PING);
|
||||
mSocket.off("dataUpdate");
|
||||
mSocket.off("announcement");
|
||||
mSocket.off("alarm");
|
||||
mSocket.off("urgent_alarm");
|
||||
mSocket.off("clear_alarm");
|
||||
|
||||
rxBus.send(new EventNSClientNewLog("NSCLIENT", "destroy"));
|
||||
isConnected = false;
|
||||
hasWriteAuth = false;
|
||||
mSocket.disconnect();
|
||||
mSocket = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void sendAuthMessage(NSAuthAck ack) {
|
||||
JSONObject authMessage = new JSONObject();
|
||||
try {
|
||||
authMessage.put("client", "Android_" + nsDevice);
|
||||
authMessage.put("history", nsHours);
|
||||
authMessage.put("status", true); // receive status
|
||||
authMessage.put("from", latestDateInReceivedData); // send data newer than
|
||||
authMessage.put("secret", nsAPIhashCode);
|
||||
} catch (JSONException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
return;
|
||||
}
|
||||
rxBus.send(new EventNSClientNewLog("AUTH", "requesting auth"));
|
||||
if (mSocket != null)
|
||||
mSocket.emit("authorize", authMessage, ack);
|
||||
}
|
||||
|
||||
public void readPreferences() {
|
||||
nsEnabled = nsClientPlugin.isEnabled(PluginType.GENERAL);
|
||||
nsURL = sp.getString(R.string.key_nsclientinternal_url, "");
|
||||
nsAPISecret = sp.getString(R.string.key_nsclientinternal_api_secret, "");
|
||||
nsDevice = sp.getString("careportal_enteredby", "");
|
||||
}
|
||||
|
||||
private final Emitter.Listener onError = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
String msg = "Unknown Error";
|
||||
if (args.length > 0 && args[0] != null) {
|
||||
msg = args[0].toString();
|
||||
}
|
||||
rxBus.send(new EventNSClientNewLog("ERROR", msg));
|
||||
}
|
||||
};
|
||||
|
||||
private final Emitter.Listener onPing = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
rxBus.send(new EventNSClientNewLog("PING", "received"));
|
||||
// send data if there is something waiting
|
||||
resend("Ping received");
|
||||
}
|
||||
};
|
||||
|
||||
private final Emitter.Listener onAnnouncement = new Emitter.Listener() {
|
||||
/*
|
||||
{
|
||||
"level":0,
|
||||
"title":"Announcement",
|
||||
"message":"test",
|
||||
"plugin":{"name":"treatmentnotify","label":"Treatment Notifications","pluginType":"notification","enabled":true},
|
||||
"group":"Announcement",
|
||||
"isAnnouncement":true,
|
||||
"key":"9ac46ad9a1dcda79dd87dae418fce0e7955c68da"
|
||||
}
|
||||
*/
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
JSONObject data;
|
||||
try {
|
||||
data = (JSONObject) args[0];
|
||||
handleAnnouncement(data);
|
||||
} catch (Exception e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Emitter.Listener onAlarm = new Emitter.Listener() {
|
||||
/*
|
||||
{
|
||||
"level":1,
|
||||
"title":"Warning HIGH",
|
||||
"message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g",
|
||||
"eventName":"high",
|
||||
"plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true},
|
||||
"pushoverSound":"climb",
|
||||
"debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}},
|
||||
"group":"default",
|
||||
"key":"simplealarms_1"
|
||||
}
|
||||
*/
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
JSONObject data;
|
||||
try {
|
||||
data = (JSONObject) args[0];
|
||||
handleAlarm(data);
|
||||
} catch (Exception e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Emitter.Listener onUrgentAlarm = args -> {
|
||||
JSONObject data;
|
||||
try {
|
||||
data = (JSONObject) args[0];
|
||||
handleUrgentAlarm(data);
|
||||
} catch (Exception e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
};
|
||||
|
||||
private final Emitter.Listener onClearAlarm = new Emitter.Listener() {
|
||||
/*
|
||||
{
|
||||
"clear":true,
|
||||
"title":"All Clear",
|
||||
"message":"default - Urgent was ack'd",
|
||||
"group":"default"
|
||||
}
|
||||
*/
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
JSONObject data;
|
||||
try {
|
||||
data = (JSONObject) args[0];
|
||||
rxBus.send(new EventNSClientNewLog("CLEARALARM", "received"));
|
||||
rxBus.send(new EventDismissNotification(Notification.NS_ALARM));
|
||||
rxBus.send(new EventDismissNotification(Notification.NS_URGENT_ALARM));
|
||||
aapsLogger.debug(LTag.NSCLIENT, data.toString());
|
||||
} catch (Exception e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Emitter.Listener onDataUpdate = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
handler.post(() -> {
|
||||
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
"AndroidAPS:NSClientService_onDataUpdate");
|
||||
wakeLock.acquire(3000);
|
||||
try {
|
||||
|
||||
JSONObject data = (JSONObject) args[0];
|
||||
try {
|
||||
// delta means only increment/changes are comming
|
||||
boolean isDelta = data.has("delta");
|
||||
rxBus.send(new EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + (isDelta ? " delta" : " full")));
|
||||
|
||||
if (data.has("status")) {
|
||||
JSONObject status = data.getJSONObject("status");
|
||||
nsSettingsStatus.handleNewData(status);
|
||||
} else if (!isDelta) {
|
||||
rxBus.send(new EventNSClientNewLog("ERROR", "Unsupported Nightscout version !!!!"));
|
||||
}
|
||||
|
||||
if (data.has("profiles")) {
|
||||
JSONArray profiles = data.getJSONArray("profiles");
|
||||
if (profiles.length() > 0) {
|
||||
// take the newest
|
||||
JSONObject profileStoreJson = (JSONObject) profiles.get(profiles.length() - 1);
|
||||
rxBus.send(new EventNSClientNewLog("PROFILE", "profile received"));
|
||||
dataWorker.enqueue(
|
||||
new OneTimeWorkRequest.Builder(NSProfilePlugin.NSProfileWorker.class)
|
||||
.setInputData(dataWorker.storeInputData(profileStoreJson, null))
|
||||
.build());
|
||||
|
||||
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("profile", profileStoreJson.toString());
|
||||
bundle.putBoolean("delta", isDelta);
|
||||
Intent intent = new Intent(Intents.ACTION_NEW_PROFILE);
|
||||
intent.putExtras(bundle);
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.has("treatments")) {
|
||||
JSONArray treatments = data.getJSONArray("treatments");
|
||||
JSONArray removedTreatments = new JSONArray();
|
||||
JSONArray addedOrUpdatedTreatments = new JSONArray();
|
||||
if (treatments.length() > 0)
|
||||
rxBus.send(new EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments"));
|
||||
for (int index = 0; index < treatments.length(); index++) {
|
||||
JSONObject jsonTreatment = treatments.getJSONObject(index);
|
||||
String action = JsonHelper.safeGetStringAllowNull(jsonTreatment, "action", null);
|
||||
long mills = JsonHelper.safeGetLong(jsonTreatment, "mills");
|
||||
|
||||
if (action == null) addedOrUpdatedTreatments.put(jsonTreatment);
|
||||
else if (action.equals("update"))
|
||||
addedOrUpdatedTreatments.put(jsonTreatment);
|
||||
else if (action.equals("remove") && mills > dateUtil.now() - T.days(1).msecs()) // handle 1 day old deletions only
|
||||
removedTreatments.put(jsonTreatment);
|
||||
}
|
||||
if (removedTreatments.length() > 0) {
|
||||
dataWorker.enqueue(
|
||||
new OneTimeWorkRequest.Builder(NSClientRemoveWorker.class)
|
||||
.setInputData(dataWorker.storeInputData(removedTreatments, null))
|
||||
.build());
|
||||
|
||||
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("treatments", removedTreatments.toString());
|
||||
bundle.putBoolean("delta", isDelta);
|
||||
Intent intent = new Intent(Intents.ACTION_REMOVED_TREATMENT);
|
||||
intent.putExtras(bundle);
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
if (addedOrUpdatedTreatments.length() > 0) {
|
||||
dataWorker.enqueue(
|
||||
new OneTimeWorkRequest.Builder(NSClientAddUpdateWorker.class)
|
||||
.setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null))
|
||||
.build());
|
||||
|
||||
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
|
||||
List<JSONArray> splitted = splitArray(addedOrUpdatedTreatments);
|
||||
for (JSONArray part : splitted) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("treatments", part.toString());
|
||||
bundle.putBoolean("delta", isDelta);
|
||||
Intent intent = new Intent(Intents.ACTION_CHANGED_TREATMENT);
|
||||
intent.putExtras(bundle);
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.has("devicestatus")) {
|
||||
JSONArray devicestatuses = data.getJSONArray("devicestatus");
|
||||
if (devicestatuses.length() > 0) {
|
||||
rxBus.send(new EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " device statuses"));
|
||||
nsDeviceStatus.handleNewData(devicestatuses);
|
||||
}
|
||||
}
|
||||
if (data.has("food")) {
|
||||
JSONArray foods = data.getJSONArray("food");
|
||||
if (foods.length() > 0)
|
||||
rxBus.send(new EventNSClientNewLog("DATA", "received " + foods.length() + " foods"));
|
||||
dataWorker.enqueue(
|
||||
new OneTimeWorkRequest.Builder(FoodPlugin.FoodWorker.class)
|
||||
.setInputData(dataWorker.storeInputData(foods, null))
|
||||
.build());
|
||||
}
|
||||
//noinspection SpellCheckingInspection
|
||||
if (data.has("mbgs")) {
|
||||
JSONArray mbgArray = data.getJSONArray("mbgs");
|
||||
if (mbgArray.length() > 0)
|
||||
rxBus.send(new EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs"));
|
||||
dataWorker.enqueue(
|
||||
new OneTimeWorkRequest.Builder(NSClientMbgWorker.class)
|
||||
.setInputData(dataWorker.storeInputData(mbgArray, null))
|
||||
.build());
|
||||
}
|
||||
if (data.has("cals")) {
|
||||
JSONArray cals = data.getJSONArray("cals");
|
||||
if (cals.length() > 0)
|
||||
rxBus.send(new EventNSClientNewLog("DATA", "received " + cals.length() + " cals"));
|
||||
// Calibrations ignored
|
||||
}
|
||||
if (data.has("sgvs")) {
|
||||
JSONArray sgvs = data.getJSONArray("sgvs");
|
||||
if (sgvs.length() > 0)
|
||||
rxBus.send(new EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs"));
|
||||
|
||||
dataWorker.enqueue(new OneTimeWorkRequest.Builder(NSClientSourcePlugin.NSClientSourceWorker.class)
|
||||
.setInputData(dataWorker.storeInputData(sgvs, null))
|
||||
.build());
|
||||
|
||||
List<JSONArray> splitted = splitArray(sgvs);
|
||||
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
|
||||
for (JSONArray part : splitted) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("sgvs", part.toString());
|
||||
bundle.putBoolean("delta", isDelta);
|
||||
Intent intent = new Intent(Intents.ACTION_NEW_SGV);
|
||||
intent.putExtras(bundle);
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
rxBus.send(new EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)));
|
||||
} catch (JSONException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
//rxBus.send(new EventNSClientNewLog("NSCLIENT", "onDataUpdate end");
|
||||
} finally {
|
||||
if (wakeLock.isHeld()) wakeLock.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
public void dbUpdate(String collection, String _id, JSONObject data, Object originalObject) {
|
||||
try {
|
||||
if (!isConnected || !hasWriteAuth) return;
|
||||
JSONObject message = new JSONObject();
|
||||
message.put("collection", collection);
|
||||
message.put("_id", _id);
|
||||
message.put("data", data);
|
||||
mSocket.emit("dbUpdate", message, new NSUpdateAck("dbUpdate", _id, aapsLogger, rxBus, originalObject));
|
||||
rxBus.send(new EventNSClientNewLog("DBUPDATE " + collection, "Sent " + originalObject.getClass().getSimpleName() + " " + _id));
|
||||
} catch (JSONException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void dbRemove(String collection, String _id, Object originalObject) {
|
||||
try {
|
||||
if (!isConnected || !hasWriteAuth) return;
|
||||
JSONObject message = new JSONObject();
|
||||
message.put("collection", collection);
|
||||
message.put("_id", _id);
|
||||
mSocket.emit("dbRemove", message, new NSUpdateAck("dbRemove", _id, aapsLogger, rxBus, originalObject));
|
||||
rxBus.send(new EventNSClientNewLog("DBREMOVE " + collection, "Sent " + originalObject.getClass().getSimpleName() + " " + _id));
|
||||
} catch (JSONException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void dbAdd(String collection, JSONObject data, Object originalObject) {
|
||||
try {
|
||||
if (!isConnected || !hasWriteAuth) return;
|
||||
JSONObject message = new JSONObject();
|
||||
message.put("collection", collection);
|
||||
message.put("data", data);
|
||||
mSocket.emit("dbAdd", message, new NSAddAck(aapsLogger, rxBus, originalObject));
|
||||
rxBus.send(new EventNSClientNewLog("DBADD " + collection, "Sent " + originalObject.getClass().getSimpleName() + " " + data));
|
||||
} catch (JSONException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendAlarmAck(AlarmAck alarmAck) {
|
||||
if (!isConnected || !hasWriteAuth) return;
|
||||
mSocket.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime);
|
||||
rxBus.send(new EventNSClientNewLog("ALARMACK ", alarmAck.level + " " + alarmAck.group + " " + alarmAck.silenceTime));
|
||||
}
|
||||
|
||||
public void resend(final String reason) {
|
||||
if (!isConnected || !hasWriteAuth) return;
|
||||
|
||||
handler.post(() -> {
|
||||
if (mSocket == null || !mSocket.connected()) return;
|
||||
|
||||
if (lastAckTime > System.currentTimeMillis() - 10 * 1000L) {
|
||||
aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastAckTime: " + ((System.currentTimeMillis() - lastAckTime) / 1000L) + " sec");
|
||||
return;
|
||||
}
|
||||
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
"AndroidAPS:NSClientService_onDataUpdate");
|
||||
wakeLock.acquire(T.mins(10).msecs());
|
||||
try {
|
||||
|
||||
rxBus.send(new EventNSClientNewLog("QUEUE", "Resend started: " + reason));
|
||||
|
||||
dataSyncSelector.processChangedBolusesCompat();
|
||||
dataSyncSelector.processChangedCarbsCompat();
|
||||
dataSyncSelector.processChangedBolusCalculatorResultsCompat();
|
||||
dataSyncSelector.processChangedTemporaryBasalsCompat();
|
||||
dataSyncSelector.processChangedExtendedBolusesCompat();
|
||||
dataSyncSelector.processChangedProfileSwitchesCompat();
|
||||
dataSyncSelector.processChangedGlucoseValuesCompat();
|
||||
dataSyncSelector.processChangedTempTargetsCompat();
|
||||
dataSyncSelector.processChangedFoodsCompat();
|
||||
dataSyncSelector.processChangedTherapyEventsCompat();
|
||||
dataSyncSelector.processChangedDeviceStatusesCompat();
|
||||
dataSyncSelector.processChangedProfileStore();
|
||||
|
||||
rxBus.send(new EventNSClientNewLog("QUEUE", "Resend ended: " + reason));
|
||||
} finally {
|
||||
if (wakeLock.isHeld()) wakeLock.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
destroy();
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void handleAnnouncement(JSONObject announcement) {
|
||||
boolean defaultVal = config.getNSCLIENT();
|
||||
if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) {
|
||||
NSAlarm nsAlarm = new NSAlarm(announcement);
|
||||
Notification notification = new NotificationWithAction(injector, nsAlarm);
|
||||
rxBus.send(new EventNewNotification(notification));
|
||||
rxBus.send(new EventNSClientNewLog("ANNOUNCEMENT", JsonHelper.safeGetString(announcement, "message", "received")));
|
||||
aapsLogger.debug(LTag.NSCLIENT, announcement.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAlarm(JSONObject alarm) {
|
||||
boolean defaultVal = config.getNSCLIENT();
|
||||
if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) {
|
||||
long snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L);
|
||||
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
|
||||
NSAlarm nsAlarm = new NSAlarm(alarm);
|
||||
Notification notification = new NotificationWithAction(injector, nsAlarm);
|
||||
rxBus.send(new EventNewNotification(notification));
|
||||
}
|
||||
rxBus.send(new EventNSClientNewLog("ALARM", JsonHelper.safeGetString(alarm, "message", "received")));
|
||||
aapsLogger.debug(LTag.NSCLIENT, alarm.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUrgentAlarm(JSONObject alarm) {
|
||||
boolean defaultVal = config.getNSCLIENT();
|
||||
if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) {
|
||||
long snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L);
|
||||
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
|
||||
NSAlarm nsAlarm = new NSAlarm(alarm);
|
||||
Notification notification = new NotificationWithAction(injector, nsAlarm);
|
||||
rxBus.send(new EventNewNotification(notification));
|
||||
}
|
||||
rxBus.send(new EventNSClientNewLog("URGENTALARM", JsonHelper.safeGetString(alarm, "message", "received")));
|
||||
aapsLogger.debug(LTag.NSCLIENT, alarm.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public List<JSONArray> splitArray(JSONArray array) {
|
||||
List<JSONArray> ret = new ArrayList<>();
|
||||
try {
|
||||
int size = array.length();
|
||||
int count = 0;
|
||||
JSONArray newarr = null;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (count == 0) {
|
||||
if (newarr != null) {
|
||||
ret.add(newarr);
|
||||
}
|
||||
newarr = new JSONArray();
|
||||
count = 20;
|
||||
}
|
||||
newarr.put(array.get(i));
|
||||
--count;
|
||||
}
|
||||
if (newarr != null && newarr.length() > 0) {
|
||||
ret.add(newarr);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
ret = new ArrayList<>();
|
||||
ret.add(array);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,750 @@
|
|||
package info.nightscout.androidaps.plugins.general.nsclient.services
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.*
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import com.google.common.base.Charsets
|
||||
import com.google.common.hash.Hashing
|
||||
import dagger.android.DaggerService
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
import info.nightscout.androidaps.events.EventAppExit
|
||||
import info.nightscout.androidaps.events.EventConfigBuilderChange
|
||||
import info.nightscout.androidaps.events.EventPreferenceChange
|
||||
import info.nightscout.androidaps.interfaces.Config
|
||||
import info.nightscout.androidaps.interfaces.DataSyncSelector
|
||||
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.food.FoodPlugin.FoodWorker
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.*
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAddAck
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSAuthAck
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.acks.NSUpdateAck
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.AlarmAck
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSAlarm
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientNewLog
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientStatus
|
||||
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationWithAction
|
||||
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin.NSProfileWorker
|
||||
import info.nightscout.androidaps.plugins.source.NSClientSourcePlugin.NSClientSourceWorker
|
||||
import info.nightscout.androidaps.receivers.DataWorker
|
||||
import info.nightscout.androidaps.services.Intents
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy
|
||||
import info.nightscout.androidaps.utils.JsonHelper.safeGetLong
|
||||
import info.nightscout.androidaps.utils.JsonHelper.safeGetString
|
||||
import info.nightscout.androidaps.utils.JsonHelper.safeGetStringAllowNull
|
||||
import info.nightscout.androidaps.utils.T.Companion.days
|
||||
import info.nightscout.androidaps.utils.T.Companion.mins
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.socket.client.IO
|
||||
import io.socket.client.Socket
|
||||
import io.socket.emitter.Emitter
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.net.URISyntaxException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class NSClientService : DaggerService() {
|
||||
|
||||
@Inject lateinit var injector: HasAndroidInjector
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var aapsSchedulers: AapsSchedulers
|
||||
@Inject lateinit var nsSettingsStatus: NSSettingsStatus
|
||||
@Inject lateinit var nsDeviceStatus: NSDeviceStatus
|
||||
@Inject lateinit var databaseHelper: DatabaseHelperInterface
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var nsClientPlugin: NSClientPlugin
|
||||
@Inject lateinit var buildHelper: BuildHelper
|
||||
@Inject lateinit var config: Config
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
@Inject lateinit var dataWorker: DataWorker
|
||||
@Inject lateinit var dataSyncSelector: DataSyncSelector
|
||||
@Inject lateinit var repository: AppRepository
|
||||
|
||||
companion object {
|
||||
private const val WATCHDOG_INTERVAL_MINUTES = 2
|
||||
private const val WATCHDOG_RECONNECT_IN = 15
|
||||
private const val WATCHDOG_MAX_CONNECTIONS = 5
|
||||
}
|
||||
|
||||
private val disposable = CompositeDisposable()
|
||||
|
||||
// public PowerManager.WakeLock mWakeLock;
|
||||
private val binder: IBinder = LocalBinder()
|
||||
private var handler: Handler? = null
|
||||
private var socket: Socket? = null
|
||||
private var dataCounter = 0
|
||||
private var connectCounter = 0
|
||||
private var nsEnabled = false
|
||||
private var nsAPISecret = ""
|
||||
private var nsDevice = ""
|
||||
private val nsHours = 48
|
||||
private var lastAckTime: Long = 0
|
||||
private var nsApiHashCode = ""
|
||||
private val reconnections = ArrayList<Long>()
|
||||
|
||||
var isConnected = false
|
||||
var hasWriteAuth = false
|
||||
var nsURL = ""
|
||||
var latestDateInReceivedData: Long = 0
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
// PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
// mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:NSClientService");
|
||||
// mWakeLock.acquire();
|
||||
initialize()
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventConfigBuilderChange::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
if (nsEnabled != nsClientPlugin.isEnabled(PluginType.GENERAL)) {
|
||||
latestDateInReceivedData = 0
|
||||
destroy()
|
||||
initialize()
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventPreferenceChange::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event: EventPreferenceChange ->
|
||||
if (event.isChanged(resourceHelper, R.string.key_nsclientinternal_url) ||
|
||||
event.isChanged(resourceHelper, R.string.key_nsclientinternal_api_secret) ||
|
||||
event.isChanged(resourceHelper, R.string.key_nsclientinternal_paused)) {
|
||||
latestDateInReceivedData = 0
|
||||
destroy()
|
||||
initialize()
|
||||
}
|
||||
}, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventAppExit::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
aapsLogger.debug(LTag.NSCLIENT, "EventAppExit received")
|
||||
destroy()
|
||||
stopSelf()
|
||||
}, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventNSClientRestart::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({
|
||||
latestDateInReceivedData = 0
|
||||
restart()
|
||||
}, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(NSAuthAck::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ ack -> processAuthAck(ack) }, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(NSUpdateAck::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ ack -> processUpdateAck(ack) }, fabricPrivacy::logException)
|
||||
)
|
||||
disposable.add(rxBus
|
||||
.toObservable(NSAddAck::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ ack -> processAddAck(ack) }, fabricPrivacy::logException)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
disposable.clear()
|
||||
// if (mWakeLock.isHeld()) mWakeLock.release();
|
||||
}
|
||||
|
||||
private fun processAddAck(ack: NSAddAck) {
|
||||
lastAckTime = dateUtil.now()
|
||||
dataWorker.enqueue(
|
||||
OneTimeWorkRequest.Builder(NSClientAddAckWorker::class.java)
|
||||
.setInputData(dataWorker.storeInputData(ack, null))
|
||||
.build())
|
||||
}
|
||||
|
||||
private fun processUpdateAck(ack: NSUpdateAck) {
|
||||
lastAckTime = dateUtil.now()
|
||||
dataWorker.enqueue(
|
||||
OneTimeWorkRequest.Builder(NSClientUpdateRemoveAckWorker::class.java)
|
||||
.setInputData(dataWorker.storeInputData(ack, null))
|
||||
.build())
|
||||
}
|
||||
|
||||
fun processAuthAck(ack: NSAuthAck) {
|
||||
var connectionStatus = "Authenticated ("
|
||||
if (ack.read) connectionStatus += "R"
|
||||
if (ack.write) connectionStatus += "W"
|
||||
if (ack.write_treatment) connectionStatus += "T"
|
||||
connectionStatus += ')'
|
||||
isConnected = true
|
||||
hasWriteAuth = ack.write && ack.write_treatment
|
||||
rxBus.send(EventNSClientStatus(connectionStatus))
|
||||
rxBus.send(EventNSClientNewLog("AUTH", connectionStatus))
|
||||
if (!ack.write) {
|
||||
rxBus.send(EventNSClientNewLog("ERROR", "Write permission not granted "))
|
||||
}
|
||||
if (!ack.write_treatment) {
|
||||
rxBus.send(EventNSClientNewLog("ERROR", "Write treatment permission not granted "))
|
||||
}
|
||||
if (!hasWriteAuth) {
|
||||
val noWritePerm = Notification(Notification.NSCLIENT_NO_WRITE_PERMISSION, resourceHelper.gs(R.string.nowritepermission), Notification.URGENT)
|
||||
rxBus.send(EventNewNotification(noWritePerm))
|
||||
} else {
|
||||
rxBus.send(EventDismissNotification(Notification.NSCLIENT_NO_WRITE_PERMISSION))
|
||||
}
|
||||
}
|
||||
|
||||
inner class LocalBinder : Binder() {
|
||||
|
||||
val serviceInstance: NSClientService
|
||||
get() = this@NSClientService
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder {
|
||||
return binder
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
fun initialize() {
|
||||
dataCounter = 0
|
||||
readPreferences()
|
||||
@Suppress("UnstableApiUsage", "DEPRECATION")
|
||||
if (nsAPISecret != "") nsApiHashCode = Hashing.sha1().hashString(nsAPISecret, Charsets.UTF_8).toString()
|
||||
rxBus.send(EventNSClientStatus("Initializing"))
|
||||
if (!nsClientPlugin.isAllowed) {
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "not allowed"))
|
||||
rxBus.send(EventNSClientStatus("Not allowed"))
|
||||
} else if (nsClientPlugin.paused) {
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "paused"))
|
||||
rxBus.send(EventNSClientStatus("Paused"))
|
||||
} else if (!nsEnabled) {
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "disabled"))
|
||||
rxBus.send(EventNSClientStatus("Disabled"))
|
||||
} else if (nsURL != "" && (buildHelper.isEngineeringMode() || nsURL.toLowerCase(Locale.getDefault()).startsWith("https://"))) {
|
||||
try {
|
||||
rxBus.send(EventNSClientStatus("Connecting ..."))
|
||||
val opt = IO.Options()
|
||||
opt.forceNew = true
|
||||
opt.reconnection = true
|
||||
socket = IO.socket(nsURL, opt).also { socket ->
|
||||
socket.on(Socket.EVENT_CONNECT, onConnect)
|
||||
socket.on(Socket.EVENT_DISCONNECT, onDisconnect)
|
||||
socket.on(Socket.EVENT_ERROR, onError)
|
||||
socket.on(Socket.EVENT_CONNECT_ERROR, onError)
|
||||
socket.on(Socket.EVENT_CONNECT_TIMEOUT, onError)
|
||||
socket.on(Socket.EVENT_PING, onPing)
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "do connect"))
|
||||
socket.connect()
|
||||
socket.on("dataUpdate", onDataUpdate)
|
||||
socket.on("announcement", onAnnouncement)
|
||||
socket.on("alarm", onAlarm)
|
||||
socket.on("urgent_alarm", onUrgentAlarm)
|
||||
socket.on("clear_alarm", onClearAlarm)
|
||||
}
|
||||
} catch (e: URISyntaxException) {
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"))
|
||||
rxBus.send(EventNSClientStatus("Wrong URL syntax"))
|
||||
} catch (e: RuntimeException) {
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "Wrong URL syntax"))
|
||||
rxBus.send(EventNSClientStatus("Wrong URL syntax"))
|
||||
}
|
||||
} else if (nsURL.toLowerCase(Locale.getDefault()).startsWith("http://")) {
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "NS URL not encrypted"))
|
||||
rxBus.send(EventNSClientStatus("Not encrypted"))
|
||||
} else {
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "No NS URL specified"))
|
||||
rxBus.send(EventNSClientStatus("Not configured"))
|
||||
}
|
||||
}
|
||||
|
||||
private val onConnect = Emitter.Listener {
|
||||
connectCounter++
|
||||
val socketId = socket?.id() ?: "NULL"
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "connect #$connectCounter event. ID: $socketId"))
|
||||
if (socket != null) sendAuthMessage(NSAuthAck(rxBus))
|
||||
watchdog()
|
||||
}
|
||||
|
||||
private fun watchdog() {
|
||||
synchronized(reconnections) {
|
||||
val now = dateUtil.now()
|
||||
reconnections.add(now)
|
||||
for (i in reconnections.indices) {
|
||||
val r = reconnections[i]
|
||||
if (r < now - mins(WATCHDOG_INTERVAL_MINUTES.toLong()).msecs()) {
|
||||
reconnections.remove(r)
|
||||
}
|
||||
}
|
||||
rxBus.send(EventNSClientNewLog("WATCHDOG", "connections in last " + WATCHDOG_INTERVAL_MINUTES + " minutes: " + reconnections.size + "/" + WATCHDOG_MAX_CONNECTIONS))
|
||||
if (reconnections.size >= WATCHDOG_MAX_CONNECTIONS) {
|
||||
val n = Notification(Notification.NS_MALFUNCTION, resourceHelper.gs(R.string.nsmalfunction), Notification.URGENT)
|
||||
rxBus.send(EventNewNotification(n))
|
||||
rxBus.send(EventNSClientNewLog("WATCHDOG", "pausing for $WATCHDOG_RECONNECT_IN minutes"))
|
||||
nsClientPlugin.pause(true)
|
||||
rxBus.send(EventNSClientUpdateGUI())
|
||||
Thread {
|
||||
SystemClock.sleep(mins(WATCHDOG_RECONNECT_IN.toLong()).msecs())
|
||||
rxBus.send(EventNSClientNewLog("WATCHDOG", "re-enabling NSClient"))
|
||||
nsClientPlugin.pause(false)
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val onDisconnect = Emitter.Listener { args ->
|
||||
aapsLogger.debug(LTag.NSCLIENT, "disconnect reason: {}", *args)
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "disconnect event"))
|
||||
}
|
||||
|
||||
@Synchronized fun destroy() {
|
||||
socket?.off(Socket.EVENT_CONNECT)
|
||||
socket?.off(Socket.EVENT_DISCONNECT)
|
||||
socket?.off(Socket.EVENT_PING)
|
||||
socket?.off("dataUpdate")
|
||||
socket?.off("announcement")
|
||||
socket?.off("alarm")
|
||||
socket?.off("urgent_alarm")
|
||||
socket?.off("clear_alarm")
|
||||
rxBus.send(EventNSClientNewLog("NSCLIENT", "destroy"))
|
||||
isConnected = false
|
||||
hasWriteAuth = false
|
||||
socket?.disconnect()
|
||||
socket = null
|
||||
}
|
||||
|
||||
private fun sendAuthMessage(ack: NSAuthAck?) {
|
||||
val authMessage = JSONObject()
|
||||
try {
|
||||
authMessage.put("client", "Android_$nsDevice")
|
||||
authMessage.put("history", nsHours)
|
||||
authMessage.put("status", true) // receive status
|
||||
authMessage.put("from", latestDateInReceivedData) // send data newer than
|
||||
authMessage.put("secret", nsApiHashCode)
|
||||
} catch (e: JSONException) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
return
|
||||
}
|
||||
rxBus.send(EventNSClientNewLog("AUTH", "requesting auth"))
|
||||
socket?.emit("authorize", authMessage, ack)
|
||||
}
|
||||
|
||||
fun readPreferences() {
|
||||
nsEnabled = nsClientPlugin.isEnabled(PluginType.GENERAL)
|
||||
nsURL = sp.getString(R.string.key_nsclientinternal_url, "")
|
||||
nsAPISecret = sp.getString(R.string.key_nsclientinternal_api_secret, "")
|
||||
nsDevice = sp.getString("careportal_enteredby", "")
|
||||
}
|
||||
|
||||
private val onError = Emitter.Listener { args ->
|
||||
var msg = "Unknown Error"
|
||||
if (args.isNotEmpty() && args[0] != null) {
|
||||
msg = args[0].toString()
|
||||
}
|
||||
rxBus.send(EventNSClientNewLog("ERROR", msg))
|
||||
}
|
||||
private val onPing = Emitter.Listener {
|
||||
rxBus.send(EventNSClientNewLog("PING", "received"))
|
||||
// send data if there is something waiting
|
||||
resend("Ping received")
|
||||
}
|
||||
private val onAnnouncement = Emitter.Listener { args ->
|
||||
|
||||
/*
|
||||
{
|
||||
"level":0,
|
||||
"title":"Announcement",
|
||||
"message":"test",
|
||||
"plugin":{"name":"treatmentnotify","label":"Treatment Notifications","pluginType":"notification","enabled":true},
|
||||
"group":"Announcement",
|
||||
"isAnnouncement":true,
|
||||
"key":"9ac46ad9a1dcda79dd87dae418fce0e7955c68da"
|
||||
}
|
||||
*/
|
||||
val data: JSONObject
|
||||
try {
|
||||
data = args[0] as JSONObject
|
||||
handleAnnouncement(data)
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
private val onAlarm = Emitter.Listener { args ->
|
||||
|
||||
/*
|
||||
{
|
||||
"level":1,
|
||||
"title":"Warning HIGH",
|
||||
"message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g",
|
||||
"eventName":"high",
|
||||
"plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true},
|
||||
"pushoverSound":"climb",
|
||||
"debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}},
|
||||
"group":"default",
|
||||
"key":"simplealarms_1"
|
||||
}
|
||||
*/
|
||||
val data: JSONObject
|
||||
try {
|
||||
data = args[0] as JSONObject
|
||||
handleAlarm(data)
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
private val onUrgentAlarm = Emitter.Listener { args: Array<Any> ->
|
||||
val data: JSONObject
|
||||
try {
|
||||
data = args[0] as JSONObject
|
||||
handleUrgentAlarm(data)
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
private val onClearAlarm = Emitter.Listener { args ->
|
||||
|
||||
/*
|
||||
{
|
||||
"clear":true,
|
||||
"title":"All Clear",
|
||||
"message":"default - Urgent was ack'd",
|
||||
"group":"default"
|
||||
}
|
||||
*/
|
||||
val data: JSONObject
|
||||
try {
|
||||
data = args[0] as JSONObject
|
||||
rxBus.send(EventNSClientNewLog("CLEARALARM", "received"))
|
||||
rxBus.send(EventDismissNotification(Notification.NS_ALARM))
|
||||
rxBus.send(EventDismissNotification(Notification.NS_URGENT_ALARM))
|
||||
aapsLogger.debug(LTag.NSCLIENT, data.toString())
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
private val onDataUpdate = Emitter.Listener { args ->
|
||||
handler?.post {
|
||||
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
|
||||
val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
"AndroidAPS:NSClientService_onDataUpdate")
|
||||
wakeLock.acquire(3000)
|
||||
try {
|
||||
val data = args[0] as JSONObject
|
||||
try {
|
||||
// delta means only increment/changes are coming
|
||||
val isDelta = data.has("delta")
|
||||
rxBus.send(EventNSClientNewLog("DATA", "Data packet #" + dataCounter++ + if (isDelta) " delta" else " full"))
|
||||
if (data.has("status")) {
|
||||
val status = data.getJSONObject("status")
|
||||
nsSettingsStatus.handleNewData(status)
|
||||
} else if (!isDelta) {
|
||||
rxBus.send(EventNSClientNewLog("ERROR", "Unsupported Nightscout version "))
|
||||
}
|
||||
if (data.has("profiles")) {
|
||||
val profiles = data.getJSONArray("profiles")
|
||||
if (profiles.length() > 0) {
|
||||
// take the newest
|
||||
val profileStoreJson = profiles[profiles.length() - 1] as JSONObject
|
||||
rxBus.send(EventNSClientNewLog("PROFILE", "profile received"))
|
||||
dataWorker.enqueue(
|
||||
OneTimeWorkRequest.Builder(NSProfileWorker::class.java)
|
||||
.setInputData(dataWorker.storeInputData(profileStoreJson, null))
|
||||
.build())
|
||||
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("profile", profileStoreJson.toString())
|
||||
bundle.putBoolean("delta", isDelta)
|
||||
val intent = Intent(Intents.ACTION_NEW_PROFILE)
|
||||
intent.putExtras(bundle)
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.has("treatments")) {
|
||||
val treatments = data.getJSONArray("treatments")
|
||||
val removedTreatments = JSONArray()
|
||||
val addedOrUpdatedTreatments = JSONArray()
|
||||
if (treatments.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments"))
|
||||
for (index in 0 until treatments.length()) {
|
||||
val jsonTreatment = treatments.getJSONObject(index)
|
||||
val action = safeGetStringAllowNull(jsonTreatment, "action", null)
|
||||
val mills = safeGetLong(jsonTreatment, "mills")
|
||||
if (action == null) addedOrUpdatedTreatments.put(jsonTreatment) else if (action == "update") addedOrUpdatedTreatments.put(jsonTreatment) else if (action == "remove" && mills > dateUtil.now() - days(1).msecs()) // handle 1 day old deletions only
|
||||
removedTreatments.put(jsonTreatment)
|
||||
}
|
||||
if (removedTreatments.length() > 0) {
|
||||
dataWorker.enqueue(
|
||||
OneTimeWorkRequest.Builder(NSClientRemoveWorker::class.java)
|
||||
.setInputData(dataWorker.storeInputData(removedTreatments, null))
|
||||
.build())
|
||||
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("treatments", removedTreatments.toString())
|
||||
bundle.putBoolean("delta", isDelta)
|
||||
val intent = Intent(Intents.ACTION_REMOVED_TREATMENT)
|
||||
intent.putExtras(bundle)
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
if (addedOrUpdatedTreatments.length() > 0) {
|
||||
dataWorker.enqueue(
|
||||
OneTimeWorkRequest.Builder(NSClientAddUpdateWorker::class.java)
|
||||
.setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null))
|
||||
.build())
|
||||
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
|
||||
val splitted = splitArray(addedOrUpdatedTreatments)
|
||||
for (part in splitted) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("treatments", part.toString())
|
||||
bundle.putBoolean("delta", isDelta)
|
||||
val intent = Intent(Intents.ACTION_CHANGED_TREATMENT)
|
||||
intent.putExtras(bundle)
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data.has("devicestatus")) {
|
||||
val devicestatuses = data.getJSONArray("devicestatus")
|
||||
if (devicestatuses.length() > 0) {
|
||||
rxBus.send(EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " device statuses"))
|
||||
nsDeviceStatus.handleNewData(devicestatuses)
|
||||
}
|
||||
}
|
||||
if (data.has("food")) {
|
||||
val foods = data.getJSONArray("food")
|
||||
if (foods.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + foods.length() + " foods"))
|
||||
dataWorker.enqueue(
|
||||
OneTimeWorkRequest.Builder(FoodWorker::class.java)
|
||||
.setInputData(dataWorker.storeInputData(foods, null))
|
||||
.build())
|
||||
}
|
||||
if (data.has("mbgs")) {
|
||||
val mbgArray = data.getJSONArray("mbgs")
|
||||
if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs"))
|
||||
dataWorker.enqueue(
|
||||
OneTimeWorkRequest.Builder(NSClientMbgWorker::class.java)
|
||||
.setInputData(dataWorker.storeInputData(mbgArray, null))
|
||||
.build())
|
||||
}
|
||||
if (data.has("cals")) {
|
||||
val cals = data.getJSONArray("cals")
|
||||
if (cals.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + cals.length() + " cals"))
|
||||
// Calibrations ignored
|
||||
}
|
||||
if (data.has("sgvs")) {
|
||||
val sgvs = data.getJSONArray("sgvs")
|
||||
if (sgvs.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs"))
|
||||
dataWorker.enqueue(OneTimeWorkRequest.Builder(NSClientSourceWorker::class.java)
|
||||
.setInputData(dataWorker.storeInputData(sgvs, null))
|
||||
.build())
|
||||
val splitted = splitArray(sgvs)
|
||||
if (sp.getBoolean(R.string.key_nsclient_localbroadcasts, false)) {
|
||||
for (part in splitted) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString("sgvs", part.toString())
|
||||
bundle.putBoolean("delta", isDelta)
|
||||
val intent = Intent(Intents.ACTION_NEW_SGV)
|
||||
intent.putExtras(bundle)
|
||||
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
rxBus.send(EventNSClientNewLog("LAST", dateUtil.dateAndTimeString(latestDateInReceivedData)))
|
||||
} catch (e: JSONException) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
}
|
||||
//rxBus.send(new EventNSClientNewLog("NSCLIENT", "onDataUpdate end");
|
||||
} finally {
|
||||
if (wakeLock.isHeld) wakeLock.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun dbUpdate(collection: String, _id: String?, data: JSONObject?, originalObject: Any) {
|
||||
try {
|
||||
if (_id == null) return
|
||||
if (!isConnected || !hasWriteAuth) return
|
||||
val message = JSONObject()
|
||||
message.put("collection", collection)
|
||||
message.put("_id", _id)
|
||||
message.put("data", data)
|
||||
socket?.emit("dbUpdate", message, NSUpdateAck("dbUpdate", _id, aapsLogger, rxBus, originalObject))
|
||||
rxBus.send(EventNSClientNewLog("DBUPDATE $collection", "Sent " + originalObject.javaClass.simpleName + " " + _id))
|
||||
} catch (e: JSONException) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun dbRemove(collection: String, _id: String?, originalObject: Any) {
|
||||
try {
|
||||
if (_id == null) return
|
||||
if (!isConnected || !hasWriteAuth) return
|
||||
val message = JSONObject()
|
||||
message.put("collection", collection)
|
||||
message.put("_id", _id)
|
||||
socket?.emit("dbRemove", message, NSUpdateAck("dbRemove", _id, aapsLogger, rxBus, originalObject))
|
||||
rxBus.send(EventNSClientNewLog("DBREMOVE $collection", "Sent " + originalObject.javaClass.simpleName + " " + _id))
|
||||
} catch (e: JSONException) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun dbAdd(collection: String, data: JSONObject, originalObject: Any) {
|
||||
try {
|
||||
if (!isConnected || !hasWriteAuth) return
|
||||
val message = JSONObject()
|
||||
message.put("collection", collection)
|
||||
message.put("data", data)
|
||||
socket?.emit("dbAdd", message, NSAddAck(aapsLogger, rxBus, originalObject))
|
||||
rxBus.send(EventNSClientNewLog("DBADD $collection", "Sent " + originalObject.javaClass.simpleName + " " + data))
|
||||
} catch (e: JSONException) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendAlarmAck(alarmAck: AlarmAck) {
|
||||
if (!isConnected || !hasWriteAuth) return
|
||||
socket?.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime)
|
||||
rxBus.send(EventNSClientNewLog("ALARMACK ", alarmAck.level.toString() + " " + alarmAck.group + " " + alarmAck.silenceTime))
|
||||
}
|
||||
|
||||
fun resend(reason: String) {
|
||||
if (!isConnected || !hasWriteAuth) return
|
||||
handler?.post {
|
||||
if (socket?.connected() != true) return@post
|
||||
if (lastAckTime > System.currentTimeMillis() - 10 * 1000L) {
|
||||
aapsLogger.debug(LTag.NSCLIENT, "Skipping resend by lastAckTime: " + (System.currentTimeMillis() - lastAckTime) / 1000L + " sec")
|
||||
return@post
|
||||
}
|
||||
val powerManager = getSystemService(POWER_SERVICE) as PowerManager
|
||||
val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||
"AndroidAPS:NSClientService_onDataUpdate")
|
||||
wakeLock.acquire(mins(10).msecs())
|
||||
try {
|
||||
rxBus.send(EventNSClientNewLog("QUEUE", "Resend started: $reason"))
|
||||
dataSyncSelector.processChangedBolusesCompat()
|
||||
dataSyncSelector.processChangedCarbsCompat()
|
||||
dataSyncSelector.processChangedBolusCalculatorResultsCompat()
|
||||
dataSyncSelector.processChangedTemporaryBasalsCompat()
|
||||
dataSyncSelector.processChangedExtendedBolusesCompat()
|
||||
dataSyncSelector.processChangedProfileSwitchesCompat()
|
||||
dataSyncSelector.processChangedGlucoseValuesCompat()
|
||||
dataSyncSelector.processChangedTempTargetsCompat()
|
||||
dataSyncSelector.processChangedFoodsCompat()
|
||||
dataSyncSelector.processChangedTherapyEventsCompat()
|
||||
dataSyncSelector.processChangedDeviceStatusesCompat()
|
||||
dataSyncSelector.processChangedProfileStore()
|
||||
rxBus.send(EventNSClientNewLog("QUEUE", "Resend ended: $reason"))
|
||||
} finally {
|
||||
if (wakeLock.isHeld) wakeLock.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun restart() {
|
||||
destroy()
|
||||
initialize()
|
||||
}
|
||||
|
||||
private fun handleAnnouncement(announcement: JSONObject) {
|
||||
val defaultVal = config.NSCLIENT
|
||||
if (sp.getBoolean(R.string.key_ns_announcements, defaultVal)) {
|
||||
val nsAlarm = NSAlarm(announcement)
|
||||
val notification: Notification = NotificationWithAction(injector, nsAlarm)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
rxBus.send(EventNSClientNewLog("ANNOUNCEMENT", safeGetString(announcement, "message", "received")))
|
||||
aapsLogger.debug(LTag.NSCLIENT, announcement.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAlarm(alarm: JSONObject) {
|
||||
val defaultVal = config.NSCLIENT
|
||||
if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) {
|
||||
val snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L)
|
||||
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
|
||||
val nsAlarm = NSAlarm(alarm)
|
||||
val notification: Notification = NotificationWithAction(injector, nsAlarm)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
}
|
||||
rxBus.send(EventNSClientNewLog("ALARM", safeGetString(alarm, "message", "received")))
|
||||
aapsLogger.debug(LTag.NSCLIENT, alarm.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUrgentAlarm(alarm: JSONObject) {
|
||||
val defaultVal = config.NSCLIENT
|
||||
if (sp.getBoolean(R.string.key_ns_alarms, defaultVal)) {
|
||||
val snoozedTo = sp.getLong(R.string.key_snoozedTo, 0L)
|
||||
if (snoozedTo == 0L || System.currentTimeMillis() > snoozedTo) {
|
||||
val nsAlarm = NSAlarm(alarm)
|
||||
val notification: Notification = NotificationWithAction(injector, nsAlarm)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
}
|
||||
rxBus.send(EventNSClientNewLog("URGENTALARM", safeGetString(alarm, "message", "received")))
|
||||
aapsLogger.debug(LTag.NSCLIENT, alarm.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun splitArray(array: JSONArray): List<JSONArray> {
|
||||
var ret: MutableList<JSONArray> = ArrayList()
|
||||
try {
|
||||
val size = array.length()
|
||||
var count = 0
|
||||
var newarr: JSONArray? = null
|
||||
for (i in 0 until size) {
|
||||
if (count == 0) {
|
||||
if (newarr != null) ret.add(newarr)
|
||||
newarr = JSONArray()
|
||||
count = 20
|
||||
}
|
||||
newarr?.put(array[i])
|
||||
--count
|
||||
}
|
||||
if (newarr != null && newarr.length() > 0) ret.add(newarr)
|
||||
} catch (e: JSONException) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
ret = ArrayList()
|
||||
ret.add(array)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
init {
|
||||
if (handler == null) {
|
||||
val handlerThread = HandlerThread(NSClientService::class.java.simpleName + "Handler")
|
||||
handlerThread.start()
|
||||
handler = Handler(handlerThread.looper)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue