mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-26 18:45:49 +01:00
Bangle.js: Tidy up data transmit and add data receive
This commit is contained in:
parent
43bce3ed80
commit
3c16b246a7
26
Banglejs.md
26
Banglejs.md
@ -1,5 +1,15 @@
|
||||
|
||||
https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Developer-Documentation
|
||||
|
||||
```Bash
|
||||
./gradlew assembleDebug
|
||||
adb install app/build/outputs/apk/debug/app-debug.apk
|
||||
```
|
||||
|
||||
Messages sent to Bangle.js from Phone
|
||||
--------------------------------------
|
||||
|
||||
wrapped in `GB(json)\n`
|
||||
|
||||
`t:"notify", id:id, src,title,subject,body,sender,tel` - new notification
|
||||
`t:"notify-", id:id` - delete notification
|
||||
@ -7,6 +17,18 @@ adb install app/build/outputs/apk/debug/app-debug.apk
|
||||
`t:"find", n:bool` - findDevice
|
||||
`t:"vibrate", n:int` - vibrate
|
||||
`t:"weather", temp,hum,txt,wind,loc` - weather report
|
||||
`t:"musicstate", state,position,shuffle,repeat`
|
||||
`t:"musicinfo", artist,album,track,dur,c(track count),n(track num)`
|
||||
|
||||
"musicstate", state,position,shuffle,repeat
|
||||
"musicinfo", artist,album,track,dur,c(track count),n(track num)
|
||||
Messages from Bangle.js to Phone
|
||||
--------------------------------
|
||||
|
||||
Just raw newline-terminated JSON lines:
|
||||
|
||||
`t:"info", msg:"..."`
|
||||
`t:"warn", msg:"..."`
|
||||
`t:"error", msg:"..."`
|
||||
`t:"status", bat:0..100, volt:float(voltage)` - status update
|
||||
`t:"findPhone", n:bool`
|
||||
`t:"music", n:"play/pause/next/previous/volumeup/volumedown"`
|
||||
`t:"call", n:"ACCEPT/END/INCOMING/OUTGOING/REJECT/START/IGNORE"`
|
||||
|
@ -1,9 +1,12 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.banglejs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
@ -11,21 +14,33 @@ import java.util.UUID;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONArray;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.banglejs.BangleJSConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Constants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.No1F1ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
||||
@ -40,7 +55,6 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BangleJSDeviceSupport.class);
|
||||
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
||||
public BluetoothGattCharacteristic rxCharacteristic = null;
|
||||
public BluetoothGattCharacteristic txCharacteristic = null;
|
||||
|
||||
@ -79,6 +93,7 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
/// Write a string of data, and chunk it up
|
||||
public void uartTx(TransactionBuilder builder, String str) {
|
||||
LOG.info("UART TX: ", str);
|
||||
byte bytes[];
|
||||
try {
|
||||
bytes = str.getBytes("UTF-8");
|
||||
@ -96,6 +111,97 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a string of data, and chunk it up
|
||||
public void uartTxJSON(String taskName, JSONObject json) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized(taskName);
|
||||
uartTx(builder, "\u0010GB("+json.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (IOException e) {
|
||||
GB.toast(getContext(), "Error in "+taskName+": " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void handleUartRxLine(String line) {
|
||||
LOG.info("UART RX LINE: " + line);
|
||||
|
||||
if (line==">Uncaught ReferenceError: \"gb\" is not defined")
|
||||
GB.toast(getContext(), "Gadgetbridge plugin not installed on Bangle.js", Toast.LENGTH_LONG, GB.ERROR);
|
||||
else if (line.charAt(0)=='{') {
|
||||
// JSON - we hope!
|
||||
try {
|
||||
JSONObject json = new JSONObject(line);
|
||||
handleUartRxJSON(json);
|
||||
} catch (JSONException e) {
|
||||
GB.toast(getContext(), "Malformed JSON from Bangle.js: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleUartRxJSON(JSONObject json) throws JSONException {
|
||||
switch (json.getString("t")) {
|
||||
case "info":
|
||||
GB.toast(getContext(), "Bangle.js: " + json.getString("msg"), Toast.LENGTH_LONG, GB.INFO);
|
||||
break;
|
||||
case "warn":
|
||||
GB.toast(getContext(), "Bangle.js: " + json.getString("msg"), Toast.LENGTH_LONG, GB.WARN);
|
||||
break;
|
||||
case "error":
|
||||
GB.toast(getContext(), "Bangle.js: " + json.getString("msg"), Toast.LENGTH_LONG, GB.ERROR);
|
||||
break;
|
||||
case "status": {
|
||||
Context context = getContext();
|
||||
if (json.has("bat")) {
|
||||
int b = json.getInt("bat");
|
||||
if (b<0) b=0;
|
||||
if (b>100) b=100;
|
||||
gbDevice.setBatteryLevel((short)b);
|
||||
if (b < 30) {
|
||||
gbDevice.setBatteryState(BatteryState.BATTERY_LOW);
|
||||
GB.updateBatteryNotification(context.getString(R.string.notif_battery_low_percent, gbDevice.getName(), String.valueOf(b)), "", context);
|
||||
} else {
|
||||
gbDevice.setBatteryState(BatteryState.BATTERY_NORMAL);
|
||||
GB.removeBatteryNotification(context);
|
||||
}
|
||||
}
|
||||
if (json.has("volt"))
|
||||
gbDevice.setBatteryVoltage((float)json.getDouble("volt"));
|
||||
gbDevice.sendDeviceUpdateIntent(context);
|
||||
} break;
|
||||
case "findPhone": {
|
||||
boolean start = json.has("n") && json.getBoolean("n");
|
||||
GBDeviceEventFindPhone deviceEventFindPhone = new GBDeviceEventFindPhone();
|
||||
deviceEventFindPhone.event = start ? GBDeviceEventFindPhone.Event.START : GBDeviceEventFindPhone.Event.STOP;
|
||||
evaluateGBDeviceEvent(deviceEventFindPhone);
|
||||
} break;
|
||||
case "music": {
|
||||
GBDeviceEventMusicControl deviceEventMusicControl = new GBDeviceEventMusicControl();
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.valueOf(json.getString("n").toUpperCase());
|
||||
evaluateGBDeviceEvent(deviceEventMusicControl);
|
||||
} break;
|
||||
case "call": {
|
||||
GBDeviceEventCallControl deviceEventCallControl = new GBDeviceEventCallControl();
|
||||
deviceEventCallControl.event = GBDeviceEventCallControl.Event.valueOf(json.getString("n").toUpperCase());
|
||||
evaluateGBDeviceEvent(deviceEventCallControl);
|
||||
} break;
|
||||
/*case "activity": {
|
||||
BangleJSActivitySample sample = new BangleJSActivitySample();
|
||||
sample.setTimestamp((int) (GregorianCalendar.getInstance().getTimeInMillis() / 1000L));
|
||||
sample.setHeartRate(json.getInteger("hrm"));
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
|
||||
Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
|
||||
BangleJSSampleProvider provider = new BangleJSSampleProvider(getDevice(), dbHandler.getDaoSession());
|
||||
sample.setDeviceId(deviceId);
|
||||
sample.setUserId(userId);
|
||||
provider.addGBActivitySample(sample);
|
||||
} catch (Exception ex) {
|
||||
LOG.warn("Error saving current heart rate: " + ex.getLocalizedMessage());
|
||||
}
|
||||
} break;*/
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic) {
|
||||
@ -111,8 +217,7 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
int p = receivedLine.indexOf("\n");
|
||||
String line = receivedLine.substring(0,p-1);
|
||||
receivedLine = receivedLine.substring(p+1);
|
||||
LOG.info("RX LINE: " + line);
|
||||
// TODO: parse this into JSON and handle it
|
||||
handleUartRxLine(line);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -120,19 +225,17 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
|
||||
|
||||
void setTime(TransactionBuilder builder) {
|
||||
|
||||
uartTx(builder, "\u0010setTime("+(System.currentTimeMillis()/1000)+");E.setTimeZone("+(TimeZone.getDefault().getRawOffset()/3600000)+");\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onNotification");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "notify");
|
||||
o.put("id", notificationSpec.getId());
|
||||
@ -142,25 +245,21 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
o.put("body", notificationSpec.body);
|
||||
o.put("sender", notificationSpec.sender);
|
||||
o.put("tel", notificationSpec.phoneNumber);
|
||||
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error setting notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onNotification", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteNotification(int id) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onDeleteNotification");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "notify-");
|
||||
o.put("id", id);
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error deleting notification: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onDeleteNotification", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,7 +277,6 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onSetAlarms");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "alarm");
|
||||
JSONArray jsonalarms = new JSONArray();
|
||||
@ -194,30 +292,26 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
jsonalarm.put("h", alarm.getHour());
|
||||
jsonalarm.put("m", alarm.getMinute());
|
||||
}
|
||||
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error setting alarms: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onSetAlarms", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetCallState(CallSpec callSpec) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onSetCallState");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "call");
|
||||
String cmdString[] = {"","undefined","accept","incoming","outgoing","reject","start","end"};
|
||||
o.put("cmd", cmdString[callSpec.command]);
|
||||
o.put("name", callSpec.name);
|
||||
o.put("number", callSpec.number);
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error setting call state: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onSetCallState", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
|
||||
@ -227,7 +321,6 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
@Override
|
||||
public void onSetMusicState(MusicStateSpec stateSpec) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onSetMusicState");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "musicstate");
|
||||
String musicStates[] = {"play","pause","stop",""};
|
||||
@ -235,17 +328,15 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
o.put("position", stateSpec.position);
|
||||
o.put("shuffle", stateSpec.shuffle);
|
||||
o.put("repeat", stateSpec.repeat);
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error setting Music state: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onSetMusicState", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onSetMusicInfo");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "musicinfo");
|
||||
o.put("artist", musicSpec.artist);
|
||||
@ -254,10 +345,9 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
o.put("dur", musicSpec.duration);
|
||||
o.put("c", musicSpec.trackCount);
|
||||
o.put("n", musicSpec.trackNr);
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error setting Music info: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onSetMusicInfo", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -319,28 +409,24 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
@Override
|
||||
public void onFindDevice(boolean start) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onFindDevice");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "find");
|
||||
o.put("n", start);
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error finding device: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onFindDevice", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetConstantVibration(int integer) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onSetConstantVibration");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "vibrate");
|
||||
o.put("n", integer);
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error vibrating: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onSetConstantVibration", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,7 +473,6 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
@Override
|
||||
public void onSendWeather(WeatherSpec weatherSpec) {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("onSendWeather");
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("t", "weather");
|
||||
o.put("temp", weatherSpec.currentTemp);
|
||||
@ -395,10 +480,9 @@ public class BangleJSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
o.put("txt", weatherSpec.currentCondition);
|
||||
o.put("wind", weatherSpec.windSpeed);
|
||||
o.put("loc", weatherSpec.location);
|
||||
uartTx(builder, "\u0010gb("+o.toString()+")\n");
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception e) {
|
||||
GB.toast(getContext(), "Error showing weather: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||
uartTxJSON("onSendWeather", o);
|
||||
} catch (JSONException e) {
|
||||
LOG.info("JSONException: " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user