mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 09:01:55 +01:00
Added support for SMA Q2 (#2215)
Hi, i applied the changes from [Emeryths fork](https://github.com/Emeryth/Gadgetbridge) to master in order to get the [SMA Q2 smartwatch](https://hackaday.io/project/85463-color-open-source-smartwatch) support upstream. It uses [his firmware](https://github.com/Emeryth/sma-q2-oss) (pretty/most popular for this device). Greatly appreciate any feedback as this is my first attempt to add a device to GB :) I read through the [tutorial](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/New-Device-Tutorial), are there other sources for adding new device support? thanks! Co-authored-by: x29a <0.x29a.0@gmail.com> Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/2215 Co-authored-by: x29a <x29a@noreply.codeberg.org> Co-committed-by: x29a <x29a@noreply.codeberg.org>
This commit is contained in:
parent
d0fc83edf7
commit
5b2ca65321
@ -69,6 +69,7 @@ vendor's servers.
|
||||
- Pebble
|
||||
- [Pebble, Steel, Time, Time Steel, Time Round, 2](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Pebble)
|
||||
- PineTime (InfiniTime Firmware)
|
||||
- [SMA](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/SMA) Q2 (SMA-Q2-OSS Firmware)
|
||||
- Teclast H10, H30
|
||||
- TLW64
|
||||
- Vibratissimo (Experimental)
|
||||
|
@ -1,6 +1,7 @@
|
||||
apply plugin: "com.android.application"
|
||||
apply plugin: "com.github.spotbugs"
|
||||
apply plugin: "pmd"
|
||||
apply plugin: 'com.google.protobuf'
|
||||
|
||||
def ABORT_ON_CHECK_FAILURE = false
|
||||
|
||||
@ -29,6 +30,7 @@ android {
|
||||
versionName "0.58.2"
|
||||
versionCode 198
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
multiDexEnabled true
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@ -98,7 +100,8 @@ dependencies {
|
||||
implementation 'com.jaredrummler:colorpicker:1.0.2'
|
||||
// implementation project(":DaoCore")
|
||||
implementation 'com.github.wax911:android-emojify:0.1.7'
|
||||
|
||||
implementation 'com.google.protobuf:protobuf-lite:3.0.0'
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
}
|
||||
|
||||
preBuild.dependsOn(":GBDaoGenerator:genSources")
|
||||
@ -153,7 +156,7 @@ task pmd(type: Pmd) {
|
||||
// this is just for spotbugs to let the plugin create the task
|
||||
sourceSets {
|
||||
main {
|
||||
java.srcDirs = []
|
||||
main.java.srcDirs += "${protobuf.generatedFilesBaseDir}/main/javalite"
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,3 +182,24 @@ tasks.withType(com.github.spotbugs.SpotBugsTask) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = 'com.google.protobuf:protoc:3.0.0'
|
||||
}
|
||||
plugins {
|
||||
javalite {
|
||||
artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
|
||||
}
|
||||
}
|
||||
generateProtoTasks {
|
||||
all().each { task ->
|
||||
task.builtins {
|
||||
remove java
|
||||
}
|
||||
task.plugins {
|
||||
javalite { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,8 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceType.fromKey;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_HIGH_PRIORITY_ID;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_ID_ERROR;
|
||||
|
||||
import androidx.multidex.MultiDex;
|
||||
|
||||
/**
|
||||
* Main Application class that initializes and provides access to certain things like
|
||||
* logging and DB access.
|
||||
@ -152,6 +154,12 @@ public class GBApplication extends Application {
|
||||
// don't do anything here, add it to onCreate instead
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
MultiDex.install(this);
|
||||
}
|
||||
|
||||
public static Logging getLogging() {
|
||||
return logging;
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.smaq2oss;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport.BASE_UUID;
|
||||
|
||||
|
||||
public class SMAQ2OSSConstants {
|
||||
|
||||
// Nordic UART Service UUID
|
||||
// public static final UUID UUID_SERVICE_SMAQ2OSS = UUID.fromString("6E400001-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
// public static final UUID UUID_CHARACTERISTIC_WRITE_NORMAL = UUID.fromString("6E400002-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
// public static final UUID UUID_CHARACTERISTIC_NOTIFY_NORMAL = UUID.fromString("6E400003-B5A3-F393-E0A9-E50E24DCCA9E");
|
||||
// SMA-Q2-OSS watch service UUID 51be0000-c182-4f3a-9359-21337bce51f6
|
||||
public static final UUID UUID_SERVICE_SMAQ2OSS = UUID.fromString("51be0001-c182-4f3a-9359-21337bce51f6");
|
||||
public static final UUID UUID_CHARACTERISTIC_WRITE_NORMAL = UUID.fromString("51be0002-c182-4f3a-9359-21337bce51f6");
|
||||
public static final UUID UUID_CHARACTERISTIC_NOTIFY_NORMAL = UUID.fromString("51be0003-c182-4f3a-9359-21337bce51f6");
|
||||
|
||||
public static final byte MSG_SET_TIME = 0x01;
|
||||
public static final byte MSG_BATTERY_STATE = 0x02;
|
||||
public static final byte MSG_MUSIC_EVENT = 0x03;
|
||||
public static final byte MSG_SET_WEATHER = 0x04;
|
||||
public static final byte MSG_SET_MUSIC_INFO = 0x05;
|
||||
public static final byte MSG_CALL_NOTIFICATION = 0x06;
|
||||
public static final byte MSG_CALL_COMMAND = 0x07;
|
||||
public static final byte MSG_NOTIFICATION = 0x08;
|
||||
|
||||
public static final byte EVT_PLAY_PAUSE = 0x00;
|
||||
public static final byte EVT_FWD = 0x01;
|
||||
public static final byte EVT_REV = 0x02;
|
||||
public static final byte EVT_VOL_UP = 0x03;
|
||||
public static final byte EVT_VOL_DOWN = 0x04;
|
||||
|
||||
public static final int MUSIC_ARTIST_MAX_LEN = 32;
|
||||
public static final int MUSIC_ALBUM_MAX_LEN = 32;
|
||||
public static final int MUSIC_TRACK_MAX_LEN = 64;
|
||||
|
||||
public static final int CALL_NAME_MAX_LEN = 32;
|
||||
public static final int CALL_NUMBER_MAX_LEN = 16;
|
||||
|
||||
public static final int NOTIFICATION_SENDER_MAX_LEN = 32;
|
||||
public static final int NOTIFICATION_SUBJECT_MAX_LEN = 32;
|
||||
public static final int NOTIFICATION_BODY_MAX_LEN = 200;
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.smaq2oss;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.le.ScanFilter;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
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 SMAQ2OSSCoordinator extends AbstractDeviceCoordinator {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SMAQ2OSSCoordinator.class);
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public Collection<? extends ScanFilter> createBLEScanFilters() {
|
||||
ParcelUuid service = new ParcelUuid(SMAQ2OSSConstants.UUID_SERVICE_SMAQ2OSS);
|
||||
ScanFilter filter = new ScanFilter.Builder().setServiceUuid(service).build();
|
||||
return Collections.singletonList(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
try {
|
||||
BluetoothDevice device = candidate.getDevice();
|
||||
String name = device.getName();
|
||||
// TODO still match for "SMA-Q2-OSS" because of backward firmware compatibility - remove eventually
|
||||
if (name != null && (name.startsWith("SMAQ2-") || name.equalsIgnoreCase("SMA-Q2-OSS"))) {
|
||||
return DeviceType.SMAQ2OSS;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LOG.error("unable to check device support", ex);
|
||||
}
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle(){
|
||||
return BONDING_STYLE_NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.SMAQ2OSS;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getPairingActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateMeasurement(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "SMA";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMusicInfo() {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,448 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.smaq2oss;
|
||||
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.UUID;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.smaq2oss.SMAQ2OSSConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
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.Weather;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.SMAQ2OSSProtos;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
public class SMAQ2OSSSupport extends AbstractBTLEDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SMAQ2OSSSupport.class);
|
||||
|
||||
public BluetoothGattCharacteristic normalWriteCharacteristic = null;
|
||||
|
||||
public SMAQ2OSSSupport() {
|
||||
super(LOG);
|
||||
addSupportedService(GattService.UUID_SERVICE_GENERIC_ACCESS);
|
||||
addSupportedService(GattService.UUID_SERVICE_GENERIC_ATTRIBUTE);
|
||||
addSupportedService(SMAQ2OSSConstants.UUID_SERVICE_SMAQ2OSS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||
normalWriteCharacteristic = getCharacteristic(SMAQ2OSSConstants.UUID_CHARACTERISTIC_WRITE_NORMAL);
|
||||
normalWriteCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
|
||||
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||
|
||||
setTime(builder)
|
||||
.setInitialized(builder);
|
||||
|
||||
getDevice().setFirmwareVersion("N/A");
|
||||
getDevice().setFirmwareVersion2("N/A");
|
||||
|
||||
builder.notify(getCharacteristic(SMAQ2OSSConstants.UUID_CHARACTERISTIC_NOTIFY_NORMAL), true);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic) {
|
||||
super.onCharacteristicChanged(gatt, characteristic);
|
||||
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
if (SMAQ2OSSConstants.UUID_CHARACTERISTIC_NOTIFY_NORMAL.equals(characteristicUUID)) {
|
||||
handleDeviceEvent(characteristic.getValue());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleDeviceEvent(byte[] value){
|
||||
if (value == null || value.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (value[0]) {
|
||||
case SMAQ2OSSConstants.MSG_MUSIC_EVENT:
|
||||
LOG.info("got music control");
|
||||
handleMusicEvent(value[1]);
|
||||
break;
|
||||
case SMAQ2OSSConstants.MSG_CALL_COMMAND:
|
||||
LOG.info("got call control");
|
||||
handleCallCommand(value[1]);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleMusicEvent(byte value){
|
||||
GBDeviceEventMusicControl deviceEventMusicControl = new GBDeviceEventMusicControl();
|
||||
|
||||
switch (value){
|
||||
case SMAQ2OSSConstants.EVT_PLAY_PAUSE:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PLAYPAUSE;
|
||||
break;
|
||||
case SMAQ2OSSConstants.EVT_FWD:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.NEXT;
|
||||
break;
|
||||
case SMAQ2OSSConstants.EVT_REV:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PREVIOUS;
|
||||
break;
|
||||
case SMAQ2OSSConstants.EVT_VOL_UP:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEUP;
|
||||
break;
|
||||
case SMAQ2OSSConstants.EVT_VOL_DOWN:
|
||||
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEDOWN;
|
||||
break;
|
||||
}
|
||||
evaluateGBDeviceEvent(deviceEventMusicControl);
|
||||
|
||||
}
|
||||
|
||||
private void handleCallCommand(byte command){
|
||||
GBDeviceEventCallControl callCmd = new GBDeviceEventCallControl();
|
||||
|
||||
switch (command){
|
||||
case CallSpec.CALL_ACCEPT:
|
||||
callCmd.event = GBDeviceEventCallControl.Event.ACCEPT;
|
||||
evaluateGBDeviceEvent(callCmd);
|
||||
break;
|
||||
case CallSpec.CALL_REJECT:
|
||||
callCmd.event = GBDeviceEventCallControl.Event.REJECT;
|
||||
evaluateGBDeviceEvent(callCmd);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
|
||||
SMAQ2OSSProtos.MessageNotification.Builder notification = SMAQ2OSSProtos.MessageNotification.newBuilder();
|
||||
|
||||
|
||||
notification.setTimestamp(getTimestamp());
|
||||
String sender = StringUtils.getFirstOf(StringUtils.getFirstOf(notificationSpec.sender,notificationSpec.phoneNumber),notificationSpec.title);
|
||||
notification.setSender(truncateUTF8(sender,SMAQ2OSSConstants.NOTIFICATION_SENDER_MAX_LEN));
|
||||
// notification.setSubject(truncateUTF8(notificationSpec.subject,SMAQ2OSSConstants.NOTIFICATION_SUBJECT_MAX_LEN));
|
||||
notification.setBody(truncateUTF8(notificationSpec.body,SMAQ2OSSConstants.NOTIFICATION_BODY_MAX_LEN));
|
||||
|
||||
|
||||
try {
|
||||
TransactionBuilder builder;
|
||||
builder = performInitialized("Sending notification");
|
||||
|
||||
builder.write(normalWriteCharacteristic,createMessage(SMAQ2OSSConstants.MSG_NOTIFICATION,notification.build().toByteArray()));
|
||||
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error sending notification", ex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteNotification(int id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetTime() {
|
||||
try {
|
||||
TransactionBuilder builder = performInitialized("time");
|
||||
setTime(builder);
|
||||
performConnected(builder.getTransaction());
|
||||
} catch(IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetCallState(CallSpec callSpec) {
|
||||
|
||||
SMAQ2OSSProtos.CallNotification.Builder callnotif = SMAQ2OSSProtos.CallNotification.newBuilder();
|
||||
|
||||
callnotif.setName(truncateUTF8(callSpec.name,SMAQ2OSSConstants.CALL_NAME_MAX_LEN));
|
||||
callnotif.setNumber(truncateUTF8(callSpec.number,SMAQ2OSSConstants.CALL_NUMBER_MAX_LEN));
|
||||
callnotif.setCommand(callSpec.command);
|
||||
|
||||
try {
|
||||
TransactionBuilder builder;
|
||||
builder = performInitialized("Sending call state");
|
||||
|
||||
builder.write(normalWriteCharacteristic,createMessage(SMAQ2OSSConstants.MSG_CALL_NOTIFICATION,callnotif.build().toByteArray()));
|
||||
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error sending call state", ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetMusicState(MusicStateSpec stateSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||
|
||||
|
||||
SMAQ2OSSProtos.MusicInfo.Builder musicInfo = SMAQ2OSSProtos.MusicInfo.newBuilder();
|
||||
|
||||
musicInfo.setArtist(truncateUTF8(musicSpec.artist,SMAQ2OSSConstants.MUSIC_ARTIST_MAX_LEN));
|
||||
musicInfo.setAlbum(truncateUTF8(musicSpec.album,SMAQ2OSSConstants.MUSIC_ALBUM_MAX_LEN));
|
||||
musicInfo.setTrack(truncateUTF8(musicSpec.track,SMAQ2OSSConstants.MUSIC_TRACK_MAX_LEN));
|
||||
|
||||
try {
|
||||
TransactionBuilder builder;
|
||||
builder = performInitialized("Sending music info");
|
||||
LOG.info(musicInfo.getArtist());
|
||||
LOG.info(musicInfo.getAlbum());
|
||||
LOG.info(musicInfo.getTrack());
|
||||
|
||||
builder.write(normalWriteCharacteristic,createMessage(SMAQ2OSSConstants.MSG_SET_MUSIC_INFO,musicInfo.build().toByteArray()));
|
||||
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error sending music info", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
// try {
|
||||
// getQueue().clear();
|
||||
//
|
||||
// TransactionBuilder builder = performInitialized("reboot");
|
||||
// builder.write(normalWriteCharacteristic, new byte[] {
|
||||
// SMAQ2OSSConstants.CMD_ID_DEVICE_RESTART, SMAQ2OSSConstants.CMD_KEY_REBOOT
|
||||
// });
|
||||
// performConnected(builder.getTransaction());
|
||||
// } catch(Exception e) {
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeartRateTest() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFindDevice(boolean start) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetConstantVibration(int integer) {
|
||||
|
||||
}
|
||||
|
||||
@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 {
|
||||
TransactionBuilder builder;
|
||||
builder = performInitialized("Sending current weather");
|
||||
|
||||
SMAQ2OSSProtos.SetWeather.Builder setWeather= SMAQ2OSSProtos.SetWeather.newBuilder();
|
||||
|
||||
setWeather.setTimestamp(weatherSpec.timestamp);
|
||||
setWeather.setCondition(weatherSpec.currentConditionCode);
|
||||
setWeather.setTemperature(weatherSpec.currentTemp-273);
|
||||
setWeather.setTemperatureMin(weatherSpec.todayMinTemp-273);
|
||||
setWeather.setTemperatureMax(weatherSpec.todayMaxTemp-273);
|
||||
setWeather.setHumidity(weatherSpec.currentHumidity);
|
||||
|
||||
for (WeatherSpec.Forecast f:weatherSpec.forecasts) {
|
||||
|
||||
SMAQ2OSSProtos.Forecast.Builder fproto = SMAQ2OSSProtos.Forecast.newBuilder();
|
||||
|
||||
fproto.setCondition(f.conditionCode);
|
||||
fproto.setTemperatureMin(f.minTemp-273);
|
||||
fproto.setTemperatureMax(f.maxTemp-273);
|
||||
|
||||
setWeather.addForecasts(fproto);
|
||||
}
|
||||
|
||||
builder.write(normalWriteCharacteristic,createMessage(SMAQ2OSSConstants.MSG_SET_WEATHER,setWeather.build().toByteArray()));
|
||||
builder.queue(getQueue());
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Error sending current weather", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void setInitialized(TransactionBuilder builder) {
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
||||
}
|
||||
|
||||
byte[] createMessage(byte msgid, byte[] data){
|
||||
|
||||
ByteBuffer buf=ByteBuffer.allocate(data.length+1);
|
||||
buf.put(msgid);
|
||||
buf.put(data);
|
||||
|
||||
return buf.array();
|
||||
}
|
||||
|
||||
String truncateUTF8(String str, int len){
|
||||
|
||||
if (str ==null)
|
||||
return new String();
|
||||
|
||||
int currLen = str.getBytes(UTF_8).length;
|
||||
|
||||
while (currLen>len-1){
|
||||
|
||||
str=str.substring(0,str.length()-1);
|
||||
currLen = str.getBytes(UTF_8).length;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
private int getTimestamp(){
|
||||
|
||||
Calendar c = GregorianCalendar.getInstance();
|
||||
|
||||
long offset = (c.get(Calendar.ZONE_OFFSET) + c.get(Calendar.DST_OFFSET));
|
||||
long ts = (c.getTimeInMillis()+ offset)/1000 ;
|
||||
|
||||
return (int)ts;
|
||||
|
||||
}
|
||||
|
||||
SMAQ2OSSSupport setTime(TransactionBuilder builder) {
|
||||
|
||||
SMAQ2OSSProtos.SetTime.Builder settime = SMAQ2OSSProtos.SetTime.newBuilder();
|
||||
settime.setTimestamp(getTimestamp());
|
||||
builder.write(normalWriteCharacteristic,createMessage(SMAQ2OSSConstants.MSG_SET_TIME,settime.build().toByteArray()));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -90,6 +90,7 @@ public enum DeviceType {
|
||||
PINETIME_JF(190, R.drawable.ic_device_pinetime, R.drawable.ic_device_pinetime_disabled, R.string.devicetype_pinetime_jf),
|
||||
MIJIA_LYWSD02(200, R.drawable.ic_device_pebble, R.drawable.ic_device_pebble_disabled, R.string.devicetype_mijia_lywsd02),
|
||||
LEFUN(210, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled, R.string.devicetype_lefun),
|
||||
SMAQ2OSS(220, R.drawable.ic_device_default, R.drawable.ic_device_default, R.string.devicetype_smaq2oss),
|
||||
ITAG(250, R.drawable.ic_device_itag, R.drawable.ic_device_itag_disabled, R.string.devicetype_itag),
|
||||
NUTMINI(251, R.drawable.ic_device_itag, R.drawable.ic_device_itag_disabled, R.string.devicetype_nut_mini),
|
||||
VIBRATISSIMO(300, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo),
|
||||
|
@ -84,6 +84,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.pinetime.PineTimeJFSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.roidmi.RoidmiSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.smaq2oss.SMAQ2OSSSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12DeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.tlw64.TLW64Support;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.um25.Support.UM25Support;
|
||||
@ -344,6 +345,8 @@ public class DeviceSupportFactory {
|
||||
case WASPOS:
|
||||
deviceSupport = new ServiceDeviceSupport(new WaspOSDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
break;
|
||||
case SMAQ2OSS:
|
||||
deviceSupport = new ServiceDeviceSupport(new SMAQ2OSSSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
case UM25:
|
||||
deviceSupport = new ServiceDeviceSupport(new UM25Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
|
||||
case DOMYOS_T540:
|
||||
|
@ -102,6 +102,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeJFCoordinat
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.QHybridCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.smaq2oss.SMAQ2OSSCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.um25.Coordinator.UM25Coordinator;
|
||||
@ -300,6 +301,7 @@ public class DeviceHelper {
|
||||
result.add(new LefunDeviceCoordinator());
|
||||
result.add(new SonySWR12DeviceCoordinator());
|
||||
result.add(new WaspOSCoordinator());
|
||||
result.add(new SMAQ2OSSCoordinator());
|
||||
result.add(new UM25Coordinator());
|
||||
result.add(new DomyosT540Cooridnator());
|
||||
|
||||
|
60
app/src/main/proto/smaq2oss.proto
Normal file
60
app/src/main/proto/smaq2oss.proto
Normal file
@ -0,0 +1,60 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option java_package = "nodomain.freeyourgadget.gadgetbridge";
|
||||
option java_outer_classname = "SMAQ2OSSProtos";
|
||||
|
||||
message SetTime
|
||||
{
|
||||
int32 timestamp = 1;
|
||||
}
|
||||
|
||||
message MusicControl
|
||||
{
|
||||
int32 music_event = 1;
|
||||
}
|
||||
|
||||
message CallControl
|
||||
{
|
||||
int32 call_event = 1;
|
||||
}
|
||||
|
||||
message Forecast
|
||||
{
|
||||
int32 condition = 1;
|
||||
int32 temperature_min = 2;
|
||||
int32 temperature_max = 3;
|
||||
}
|
||||
|
||||
message SetWeather
|
||||
{
|
||||
int32 timestamp = 1;
|
||||
int32 condition = 2;
|
||||
int32 temperature = 3;
|
||||
int32 temperature_min = 4;
|
||||
int32 temperature_max = 5;
|
||||
int32 humidity = 6;
|
||||
repeated Forecast forecasts = 7;
|
||||
}
|
||||
|
||||
message MessageNotification
|
||||
{
|
||||
uint32 timestamp = 1;
|
||||
int32 type = 2;
|
||||
string sender = 3;
|
||||
string subject = 4;
|
||||
string body = 5;
|
||||
}
|
||||
|
||||
message CallNotification
|
||||
{
|
||||
string number = 1;
|
||||
string name = 2;
|
||||
int32 command = 3;
|
||||
}
|
||||
|
||||
message MusicInfo
|
||||
{
|
||||
string artist = 1;
|
||||
string album = 2;
|
||||
string track = 3;
|
||||
}
|
@ -870,6 +870,7 @@
|
||||
<string name="devicetype_pinetime_jf">PineTime (JF Firmware)</string>
|
||||
<string name="devicetype_sonyswr12" translatable="false">Sony SWR12</string>
|
||||
<string name="devicetype_waspos">Wasp-os</string>
|
||||
<string name="devicetype_smaq2oss">SMA-Q2 OSS</string>
|
||||
<string name="devicetype_domyos_t540"/>
|
||||
<string name="choose_auto_export_location">Choose export location</string>
|
||||
<string name="notification_channel_name">General</string>
|
||||
|
@ -12,6 +12,7 @@ buildscript {
|
||||
classpath 'com.android.tools.build:gradle:4.2.1'
|
||||
|
||||
classpath 'gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:2.0.0'
|
||||
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.8'
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user