diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/waspos/WaspOSConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/waspos/WaspOSConstants.java new file mode 100644 index 000000000..df5c29378 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/waspos/WaspOSConstants.java @@ -0,0 +1,28 @@ +/* Copyright (C) 2019-2020 Gordon Williams + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.waspos; + +import java.util.UUID; + +public final class WaspOSConstants { + + + public static final UUID UUID_SERVICE_NORDIC_UART = UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"); + public static final UUID UUID_CHARACTERISTIC_NORDIC_UART_TX = UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"); + public static final UUID UUID_CHARACTERISTIC_NORDIC_UART_RX = UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e"); + +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/waspos/WaspOSCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/waspos/WaspOSCoordinator.java new file mode 100644 index 000000000..772868713 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/waspos/WaspOSCoordinator.java @@ -0,0 +1,167 @@ +/* Copyright (C) 2016-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele + Gobbetti, Gordon Williams, José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.devices.waspos; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.bluetooth.le.ScanFilter; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.os.ParcelUuid; + +import androidx.annotation.NonNull; + +import java.util.Collection; +import java.util.Collections; + +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler; +import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; +import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSConstants; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate; +import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; +import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; + +public class WaspOSCoordinator extends AbstractDeviceCoordinator { + + @Override + public DeviceType getDeviceType() { + return DeviceType.WASPOS; + } + + @Override + public String getManufacturer() { + return "MicroPython"; + } + + @NonNull + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Collection createBLEScanFilters() { + // TODO: filter on name beginning Bangle.js? Doesn't appear to be built-in :( + // https://developer.android.com/reference/android/bluetooth/le/ScanFilter.Builder.html#setDeviceName(java.lang.String) + ParcelUuid hpService = new ParcelUuid(WaspOSConstants.UUID_SERVICE_NORDIC_UART); + ScanFilter filter = new ScanFilter.Builder().setServiceUuid(hpService).build(); + return Collections.singletonList(filter); + } + + @NonNull + @Override + public DeviceType getSupportedType(GBDeviceCandidate candidate) { + String name = candidate.getDevice().getName(); + /* Filter by Espruino devices to avoid getting + the device chooser full of spam devices. */ + if (name != null && ( + name.startsWith("DS-D6") || + name.startsWith("K9") || + name.startsWith("PineTime") || + name.startsWith("P8"))) + return DeviceType.WASPOS; + + return DeviceType.UNKNOWN; + } + + @Override + public int getBondingStyle(){ + return BONDING_STYLE_NONE; + } + + @Override + public boolean supportsCalendarEvents() { + return false; + } + + @Override + public boolean supportsRealtimeData() { + return false; + } + + @Override + public boolean supportsWeather() { + return false; + } + + @Override + public boolean supportsFindDevice() { + return true; + } + + @Override + public boolean supportsActivityDataFetching() { + return false; + } + + @Override + public boolean supportsActivityTracking() { + return false; + } + + @Override + public boolean supportsScreenshots() { + return false; + } + + @Override + public boolean supportsSmartWakeup(GBDevice device) { + return false; + } + + @Override + public boolean supportsHeartRateMeasurement(GBDevice device) { + return false; + } + + @Override + public boolean supportsAppsManagement() { + return false; + } + + @Override + public int getAlarmSlotCount() { + return 0; + } + + @Override + public Class getAppsManagementActivity() { + return null; + } + + + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) { + } + + @Override + public Class getPairingActivity() { + return null; + } + + @Override + public SampleProvider getSampleProvider(GBDevice device, DaoSession session) { + return null;//new WaspOSSampleProvider(device, session); + } + + @Override + public InstallHandler findInstallHandler(Uri uri, Context context) { + return null; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index a71d3980e..7fdb68f14 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -91,6 +91,7 @@ public enum DeviceType { VIBRATISSIMO(300, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo), SONY_SWR12(310, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_sonyswr12), LIVEVIEW(320, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_liveview), + WASPOS(330, R.drawable.ic_device_pebble, R.drawable.ic_device_pebble_disabled, R.string.devicetype_waspos), TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test); private final int key; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java index bdf0a947a..95cb6c3f5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java @@ -81,6 +81,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.roidmi.RoidmiSupport import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.tlw64.TLW64Support; import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.waspos.WaspOSDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.Watch9DeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.xwatch.XWatchSupport; import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.zeppe.ZeppESupport; @@ -320,6 +321,8 @@ public class DeviceSupportFactory { break; case SONY_SWR12: deviceSupport = new ServiceDeviceSupport(new SonySWR12DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); + case WASPOS: + deviceSupport = new ServiceDeviceSupport(new WaspOSDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING)); break; } if (deviceSupport != null) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/waspos/WaspOSDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/waspos/WaspOSDeviceSupport.java new file mode 100644 index 000000000..39fbde24f --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/waspos/WaspOSDeviceSupport.java @@ -0,0 +1,507 @@ +/* Copyright (C) 2019-2020 Andreas Shimokawa, Gordon Williams + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.service.devices.waspos; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.Context; +import android.net.Uri; +import android.widget.Toast; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.SimpleTimeZone; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl; +import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl; +import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSConstants; +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; +import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec; +import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec; +import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; +import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils; +import nodomain.freeyourgadget.gadgetbridge.util.GB; +import nodomain.freeyourgadget.gadgetbridge.util.Prefs; + +public class WaspOSDeviceSupport extends AbstractBTLEDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(WaspOSDeviceSupport.class); + private BluetoothGattCharacteristic rxCharacteristic = null; + private BluetoothGattCharacteristic txCharacteristic = null; + + private String receivedLine = ""; + + public WaspOSDeviceSupport() { + super(LOG); + addSupportedService(WaspOSConstants.UUID_SERVICE_NORDIC_UART); + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + LOG.info("Initializing"); + + gbDevice.setState(GBDevice.State.INITIALIZING); + gbDevice.sendDeviceUpdateIntent(getContext()); + + rxCharacteristic = getCharacteristic(WaspOSConstants.UUID_CHARACTERISTIC_NORDIC_UART_RX); + txCharacteristic = getCharacteristic(WaspOSConstants.UUID_CHARACTERISTIC_NORDIC_UART_TX); + builder.setGattCallback(this); + builder.notify(rxCharacteristic, true); + + uartTx(builder, " \u0003"); // clear active line + + Prefs prefs = GBApplication.getPrefs(); + if (prefs.getBoolean("datetime_synconconnect", true)) + setTime(builder); + //sendSettings(builder); + + // get version + + gbDevice.setState(GBDevice.State.INITIALIZED); + gbDevice.sendDeviceUpdateIntent(getContext()); + + LOG.info("Initialization Done"); + + return builder; + } + + /// Write a string of data, and chunk it up + private void uartTx(TransactionBuilder builder, String str) { + LOG.info("UART TX: " + str); + byte[] bytes; + bytes = str.getBytes(StandardCharsets.ISO_8859_1); + for (int i=0;i8) l=8; + byte[] packet = new byte[l]; + System.arraycopy(bytes, i, packet, 0, l); + builder.write(txCharacteristic, packet); + } + } + + /// Write a string of data, and chunk it up + private 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); + } + } + + private void handleUartRxLine(String line) { + LOG.info("UART RX LINE: " + line); + + if (">Uncaught ReferenceError: \"gb\" is not defined".equals(line)) + 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); + } + } + } + + private 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 "notify" : { + GBDeviceEventNotificationControl deviceEvtNotificationControl = new GBDeviceEventNotificationControl(); + // .title appears unused + deviceEvtNotificationControl.event = GBDeviceEventNotificationControl.Event.valueOf(json.getString("n").toUpperCase()); + if (json.has("id")) + deviceEvtNotificationControl.handle = json.getInt("id"); + if (json.has("tel")) + deviceEvtNotificationControl.phoneNumber = json.getString("tel"); + if (json.has("msg")) + deviceEvtNotificationControl.reply = json.getString("msg"); + evaluateGBDeviceEvent(deviceEvtNotificationControl); + } break; + /*case "activity": { + WaspOSActivitySample sample = new WaspOSActivitySample(); + 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(); + WaspOSSampleProvider provider = new WaspOSSampleProvider(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) { + if (super.onCharacteristicChanged(gatt, characteristic)) { + return true; + } + if (WaspOSConstants.UUID_CHARACTERISTIC_NORDIC_UART_RX.equals(characteristic.getUuid())) { + byte[] chars = characteristic.getValue(); + String packetStr = new String(chars); + LOG.info("RX: " + packetStr); + receivedLine += packetStr; + while (receivedLine.contains("\n")) { + int p = receivedLine.indexOf("\n"); + String line = receivedLine.substring(0,p-1); + receivedLine = receivedLine.substring(p+1); + handleUartRxLine(line); + } + } + return false; + } + + + void setTime(TransactionBuilder builder) { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("(yyyy, MM, dd, HH, mm, ss)"); + LocalDateTime now = LocalDateTime.now(); + String cmd = "\u0010watch.rtc.set_localtime("+dtf.format(now)+")\n"; + uartTx(builder, cmd+"\n"); + } + + @Override + public boolean useAutoConnect() { + return true; + } + + @Override + public void onNotification(NotificationSpec notificationSpec) { + try { + JSONObject o = new JSONObject(); + o.put("t", "notify"); + o.put("id", notificationSpec.getId()); + o.put("src", notificationSpec.sourceName); + o.put("title", notificationSpec.title); + o.put("subject", notificationSpec.subject); + o.put("body", notificationSpec.body); + o.put("sender", notificationSpec.sender); + o.put("tel", notificationSpec.phoneNumber); + uartTxJSON("onNotification", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } + } + + @Override + public void onDeleteNotification(int id) { + try { + JSONObject o = new JSONObject(); + o.put("t", "notify-"); + o.put("id", id); + uartTxJSON("onDeleteNotification", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } + } + + @Override + public void onSetTime() { + try { + TransactionBuilder builder = performInitialized("setTime"); + setTime(builder); + builder.queue(getQueue()); + } catch (Exception e) { + GB.toast(getContext(), "Error setting time: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR); + } + } + + @Override + public void onSetAlarms(ArrayList alarms) { + try { + JSONObject o = new JSONObject(); + o.put("t", "alarm"); + JSONArray jsonalarms = new JSONArray(); + o.put("d", jsonalarms); + + for (Alarm alarm : alarms) { + if (!alarm.getEnabled()) continue; + JSONObject jsonalarm = new JSONObject(); + jsonalarms.put(jsonalarm); + + Calendar calendar = AlarmUtils.toCalendar(alarm); + // TODO: getRepetition to ensure it only happens on correct day? + jsonalarm.put("h", alarm.getHour()); + jsonalarm.put("m", alarm.getMinute()); + } + uartTxJSON("onSetAlarms", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } + } + + @Override + public void onSetCallState(CallSpec callSpec) { + try { + 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); + uartTxJSON("onSetCallState", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } +} + + @Override + public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) { + + } + + @Override + public void onSetMusicState(MusicStateSpec stateSpec) { + try { + JSONObject o = new JSONObject(); + o.put("t", "musicstate"); + String[] musicStates = {"play", "pause", "stop", ""}; + o.put("state", musicStates[stateSpec.state]); + o.put("position", stateSpec.position); + o.put("shuffle", stateSpec.shuffle); + o.put("repeat", stateSpec.repeat); + uartTxJSON("onSetMusicState", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } + } + + @Override + public void onSetMusicInfo(MusicSpec musicSpec) { + try { + JSONObject o = new JSONObject(); + o.put("t", "musicinfo"); + o.put("artist", musicSpec.artist); + o.put("album", musicSpec.album); + o.put("track", musicSpec.track); + o.put("dur", musicSpec.duration); + o.put("c", musicSpec.trackCount); + o.put("n", musicSpec.trackNr); + uartTxJSON("onSetMusicInfo", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } + } + + @Override + public void onEnableRealtimeSteps(boolean enable) { + + } + + @Override + public void onInstallApp(Uri uri) { + + } + + @Override + public void onAppInfoReq() { + + } + + @Override + public void onAppStart(UUID uuid, boolean start) { + + } + + @Override + public void onAppDelete(UUID uuid) { + + } + + @Override + public void onAppConfiguration(UUID appUuid, String config, Integer id) { + + } + + @Override + public void onAppReorder(UUID[] uuids) { + + } + + @Override + public void onFetchRecordedData(int dataTypes) { + + } + + @Override + public void onReset(int flags) { + + } + + @Override + public void onHeartRateTest() { + + } + + @Override + public void onEnableRealtimeHeartRateMeasurement(boolean enable) { + + } + + @Override + public void onFindDevice(boolean start) { + try { + JSONObject o = new JSONObject(); + o.put("t", "find"); + o.put("n", start); + uartTxJSON("onFindDevice", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } + } + + @Override + public void onSetConstantVibration(int integer) { + try { + JSONObject o = new JSONObject(); + o.put("t", "vibrate"); + o.put("n", integer); + uartTxJSON("onSetConstantVibration", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } + } + + @Override + public void onScreenshotReq() { + + } + + @Override + public void onEnableHeartRateSleepSupport(boolean enable) { + + } + + @Override + public void onSetHeartRateMeasurementInterval(int seconds) { + + } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + + } + + @Override + public void onSendConfiguration(String config) { + + } + + @Override + public void onReadConfiguration(String config) { + + } + + @Override + public void onTestNewFunction() { + + } + + @Override + public void onSendWeather(WeatherSpec weatherSpec) { + try { + JSONObject o = new JSONObject(); + o.put("t", "weather"); + o.put("temp", weatherSpec.currentTemp); + o.put("hum", weatherSpec.currentHumidity); + o.put("txt", weatherSpec.currentCondition); + o.put("wind", weatherSpec.windSpeed); + o.put("loc", weatherSpec.location); + uartTxJSON("onSendWeather", o); + } catch (JSONException e) { + LOG.info("JSONException: " + e.getLocalizedMessage()); + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java index 1fa421504..1662253bd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java @@ -99,6 +99,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.waspos.WaspOSCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.xwatch.XWatchCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeCoordinator; @@ -286,6 +287,7 @@ public class DeviceHelper { result.add(new SG2Coordinator()); result.add(new LefunDeviceCoordinator()); result.add(new SonySWR12DeviceCoordinator()); + result.add(new WaspOSCoordinator()); return result; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f61bc75a3..2884bdd53 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -837,6 +837,7 @@ TLW64 PineTime (JF Firmware) Sony SWR12 + Wasp OS Choose export location Gadgetbridge notifications Gadgetbridge notifications high priority