1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-08 22:28:20 +02:00

Add initial support for Casio GB-6900B

This commit is contained in:
Andreas Böhler 2018-12-28 13:27:03 +01:00 committed by Carsten Pfeiffer
parent 5b4fd32ac1
commit 2c2d07067d
9 changed files with 931 additions and 0 deletions

View File

@ -0,0 +1,70 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
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 <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900;
import java.util.UUID;
public final class CasioGB6900Constants {
public static final UUID CASIO_VIRTUAL_SERVER_SERVICE = UUID.fromString("26eb0007-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CASIO_VIRTUAL_SERVER_FEATURES = UUID.fromString("26eb0008-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CASIO_A_NOT_W_REQ_NOT = UUID.fromString( "26eb0009-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CASIO_A_NOT_COM_SET_NOT = UUID.fromString( "26eb000a-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
// Alert
public static final UUID ALERT_SERVICE_UUID = UUID.fromString("26eb0000-b012-49a8-b1f8-394fb2032b0f");
public static final UUID ALERT_CHARACTERISTIC_UUID = UUID.fromString("00002a46-0000-1000-8000-00805f9b34fb");
public static final UUID ALERT_NOTIFICATION_CONTROL_POINT = UUID.fromString("00002a44-0000-1000-8000-00805f9b34fb");
// Phone Alert
public static final UUID CASIO_PHONE_ALERT_STATUS_SERVICE = UUID.fromString("26eb0001-b012-49a8-b1f8-394fb2032b0f");
public static final UUID RINGER_CONTROL_POINT = UUID.fromString("00002a40-0000-1000-8000-00805f9b34fb");
// Phone Finder
public static final UUID CASIO_IMMEDIATE_ALERT_SERVICE_UUID = UUID.fromString("26eb0005-b012-49a8-b1f8-394fb2032b0f");
public static final UUID ALERT_LEVEL_CHARACTERISTIC_UUID = UUID.fromString("00002a06-0000-1000-8000-00805f9b34fb");
// Current Time
public static final UUID CURRENT_TIME_SERVICE_UUID = UUID.fromString("26eb0002-b012-49a8-b1f8-394fb2032b0f");
public static final UUID CURRENT_TIME_CHARACTERISTIC_UUID = UUID.fromString("00002a2b-0000-1000-8000-00805f9b34fb");
public static final UUID LOCAL_TIME_CHARACTERISTIC_UUID = UUID.fromString("00002a0f-0000-1000-8000-00805f9b34fb");
// Control Mode
public static final UUID WATCH_FEATURES_SERVICE_UUID = UUID.fromString("26eb000d-b012-49a8-b1f8-394fb2032b0f");
public static final UUID WATCH_CTRL_SERVICE_UUID = UUID.fromString("26eb0018-b012-49a8-b1f8-394fb2032b0f");
public static final UUID KEY_CONTAINER_CHARACTERISTIC_UUID = UUID.fromString("26eb0019-b012-49a8-b1f8-394fb2032b0f");
public static final UUID NAME_OF_APP_CHARACTERISTIC_UUID = UUID.fromString("26eb001d-b012-49a8-b1f8-394fb2032b0f");
public static final UUID FUNCTION_SWITCH_CHARACTERISTIC = UUID.fromString("26eb001e-b012-49a8-b1f8-394fb2032b0f");
public static final String MUSIC_MESSAGE = "Music";
// Notification Types
public static final byte CALL_NOTIFICATION_ID = 3;
public static final byte MAIL_NOTIFICATION_ID = 1;
public static final byte CALENDAR_NOTIFICATION_ID = 7;
public static final byte SNS_NOTIFICATION_ID = 13;
public static final byte SMS_NOTIFICATION_ID = 5;
}

View File

@ -0,0 +1,158 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
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 <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 CasioGB6900DeviceCoordinator extends AbstractDeviceCoordinator {
protected static final Logger LOG = LoggerFactory.getLogger(CasioGB6900DeviceCoordinator.class);
@NonNull
@Override
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
if (candidate.supportsService(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_SERVICE)) {
return DeviceType.CASIOGB6900;
}
String name = candidate.getDevice().getName();
if (name != null) {
if (name.startsWith("CASIO")) {
return DeviceType.CASIOGB6900;
}
}
return DeviceType.UNKNOWN;
}
@Override
public int getBondingStyle(GBDevice deviceCandidate){
return BONDING_STYLE_BOND;
}
@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 DeviceType getDeviceType() {
return DeviceType.CASIOGB6900;
}
@Override
public Class<? extends Activity> getPairingActivity() {
return null;
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
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 boolean supportsScreenshots() {
return false;
}
@Override
public boolean supportsAlarmConfiguration() {
return false;
}
@Override
public int getAlarmSlotCount() {
return 5; // 4 regular and one snooze but not yet implemented
}
@Override
public boolean supportsSmartWakeup(GBDevice device) {
return false;
}
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return false;
}
@Override
public String getManufacturer() {
return "Casio";
}
@Override
public boolean supportsAppsManagement() {
return false;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return null;
}
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
}
}

View File

@ -51,6 +51,7 @@ public enum DeviceType {
WATCH9(100, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_watch9),
ROIDMI(110, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi),
ROIDMI3(112, R.drawable.ic_device_roidmi, R.drawable.ic_device_roidmi_disabled, R.string.devicetype_roidmi3),
CASIOGB6900(120, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_casiogb6900),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
private final int key;

View File

@ -29,6 +29,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900.CasioGB6900DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitcor.AmazfitCorSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband3.MiBand3Support;
@ -171,6 +172,9 @@ public class DeviceSupportFactory {
case ROIDMI3:
deviceSupport = new ServiceDeviceSupport(new RoidmiSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
case CASIOGB6900:
deviceSupport = new ServiceDeviceSupport(new CasioGB6900DeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
}
if (deviceSupport != null) {
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);

View File

@ -0,0 +1,163 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
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 <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.CountDownLatch;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
class CasioGATTServer extends BluetoothGattServerCallback {
private static final Logger LOG = LoggerFactory.getLogger(CasioGATTServer.class);
private Context mContext;
private BluetoothGattServer mBluetoothGattServer;
private CasioGB6900DeviceSupport mDeviceSupport = null;
private final GBDeviceEventMusicControl musicCmd = new GBDeviceEventMusicControl();
CasioGATTServer(Context context, CasioGB6900DeviceSupport deviceSupport) {
mContext = context;
mDeviceSupport = deviceSupport;
}
public void setContext(Context ctx)
{
mContext = ctx;
}
boolean initialize() {
BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager == null) {
return false;
}
mBluetoothGattServer = bluetoothManager.openGattServer(mContext, this);
if (mBluetoothGattServer == null) {
return false;
}
BluetoothGattService casioGATTService = new BluetoothGattService(CasioGB6900Constants.WATCH_CTRL_SERVICE_UUID, BluetoothGattService.SERVICE_TYPE_PRIMARY);
BluetoothGattCharacteristic bluetoothgGATTCharacteristic = new BluetoothGattCharacteristic(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE, BluetoothGattCharacteristic.PERMISSION_WRITE);
bluetoothgGATTCharacteristic.setValue(new byte[0]);
BluetoothGattCharacteristic bluetoothgGATTCharacteristic2 = new BluetoothGattCharacteristic(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID, BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
bluetoothgGATTCharacteristic2.setValue(CasioGB6900Constants.MUSIC_MESSAGE.getBytes());
BluetoothGattDescriptor bluetoothGattDescriptor = new BluetoothGattDescriptor(CasioGB6900Constants.CCC_DESCRIPTOR_UUID, BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothgGATTCharacteristic2.addDescriptor(bluetoothGattDescriptor);
casioGATTService.addCharacteristic(bluetoothgGATTCharacteristic);
casioGATTService.addCharacteristic(bluetoothgGATTCharacteristic2);
mBluetoothGattServer.addService(casioGATTService);
return true;
}
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
if (!characteristic.getUuid().equals(CasioGB6900Constants.NAME_OF_APP_CHARACTERISTIC_UUID)) {
LOG.warn("unexpected read request");
return;
}
LOG.info("will send response to read request from device: " + device.getAddress());
if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, CasioGB6900Constants.MUSIC_MESSAGE.getBytes())) {
LOG.warn("error sending response");
}
}
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic,
boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
if (!characteristic.getUuid().equals(CasioGB6900Constants.KEY_CONTAINER_CHARACTERISTIC_UUID)) {
LOG.warn("unexpected write request");
return;
}
if((value[0] & 0x03) == 0)
{
int button = value[1] & 0x0f;
LOG.info("Button pressed: " + button);
switch(button)
{
case 2:
musicCmd.event = GBDeviceEventMusicControl.Event.PREVIOUS;
break;
case 1:
musicCmd.event = GBDeviceEventMusicControl.Event.PLAYPAUSE;
break;
case 0:
musicCmd.event = GBDeviceEventMusicControl.Event.NEXT;
break;
}
mDeviceSupport.evaluateGBDeviceEvent(musicCmd);
}
else
{
LOG.info("received from device: " + value.toString());
}
}
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
LOG.info("Connection state change for device: " + device.getAddress() + " status = " + status + " newState = " + newState);
if (newState == BluetoothGattServer.STATE_DISCONNECTED) {
}
}
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor,
boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
LOG.info("onDescriptorWriteRequest() notifications enabled = " + (value[0] == 1));
if (!this.mBluetoothGattServer.sendResponse(device, requestId, 0, offset, value)) {
LOG.warn("onDescriptorWriteRequest() error sending response!");
}
}
public void onServiceAdded(int status, BluetoothGattService service) {
LOG.info("onServiceAdded() status = " + status + " service = " + service.getUuid());
}
public void onNotificationSent(BluetoothDevice bluetoothDevice, int status) {
LOG.info("onNotificationSent() status = " + status + " to device " + bluetoothDevice.getAddress());
}
void close() {
if (mBluetoothGattServer != null) {
mBluetoothGattServer.clearServices();
mBluetoothGattServer.close();
}
}
}

View File

@ -0,0 +1,48 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
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 <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.content.Context;
public class CasioGATTThread extends Thread {
CasioGATTServer mServer = null;
public CasioGATTThread(Context context, CasioGB6900DeviceSupport deviceSupport)
{
mServer = new CasioGATTServer(context, deviceSupport);
}
public void setContext(Context ctx)
{
mServer.setContext(ctx);
}
@Override
public void run()
{
mServer.initialize();
while(true)
{
try {
wait(100);
} catch(Exception e)
{
}
}
}
}

View File

@ -0,0 +1,484 @@
/* Copyright (C) 2018 Andreas Böhler
based on code from BlueWatcher, https://github.com/masterjc/bluewatcher
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 <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.casiogb6900;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Context;
import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900Constants;
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.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class CasioGB6900DeviceSupport extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(CasioGB6900DeviceSupport.class);
public BluetoothGattCharacteristic mCasioCharact1 = null;
public BluetoothGattCharacteristic mCasioCharact2 = null;
public BluetoothGattCharacteristic mCasioCharact3 = null;
public BluetoothGattCharacteristic mCasioCharact4 = null;
public BluetoothGattCharacteristic mCasioCharact5 = null;
private CasioGATTThread mThread = null;
public CasioGB6900DeviceSupport() {
super(LOG);
addSupportedService(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_SERVICE);
addSupportedService(CasioGB6900Constants.ALERT_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.CASIO_IMMEDIATE_ALERT_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.CURRENT_TIME_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.WATCH_CTRL_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.WATCH_FEATURES_SERVICE_UUID);
addSupportedService(CasioGB6900Constants.CASIO_PHONE_ALERT_STATUS_SERVICE);
mThread = new CasioGATTThread(getContext(), this);
}
@Override
public void setContext(GBDevice gbDevice, BluetoothAdapter btAdapter, Context context) {
super.setContext(gbDevice, btAdapter, context);
mThread.setContext(context);
mThread.start();
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
LOG.info("Initializing");
gbDevice.setState(GBDevice.State.INITIALIZING);
gbDevice.sendDeviceUpdateIntent(getContext());
mCasioCharact1 = getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT);
mCasioCharact2 = getCharacteristic(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT);
mCasioCharact3 = getCharacteristic(CasioGB6900Constants.FUNCTION_SWITCH_CHARACTERISTIC);
mCasioCharact4 = getCharacteristic(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID);
mCasioCharact5 = getCharacteristic(CasioGB6900Constants.RINGER_CONTROL_POINT);
builder.setGattCallback(this);
builder.notify(mCasioCharact1, true);
builder.notify(mCasioCharact2, true);
builder.notify(mCasioCharact3, true);
builder.notify(mCasioCharact4, true);
builder.notify(mCasioCharact5, true);
LOG.info("Initialization Done");
return builder;
}
private void writeCasioCurrentTime(TransactionBuilder builder)
{
byte[] arr = new byte[10];
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
arr[0] = (byte)((year >>> 0) & 0xff);
arr[1] = (byte)((year >>> 8) & 0xff);
arr[2] = (byte)(1 + cal.get(Calendar.MONTH));
arr[3] = (byte)cal.get(Calendar.DAY_OF_MONTH);
arr[4] = (byte)cal.get(Calendar.HOUR_OF_DAY);
arr[5] = (byte)cal.get(Calendar.MINUTE);
arr[6] = (byte)(1 + cal.get(Calendar.SECOND));
byte dayOfWk = (byte)(cal.get(Calendar.DAY_OF_WEEK) - 1);
if(dayOfWk == 0)
dayOfWk = 7;
arr[7] = dayOfWk;
arr[8] = (byte)(int) TimeUnit.MILLISECONDS.toSeconds(256 * cal.get(Calendar.MILLISECOND));
arr[9] = 1; // or 0?
BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.CURRENT_TIME_CHARACTERISTIC_UUID);
if(charact != null) {
charact.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
builder.write(charact, arr);
}
else {
LOG.warn("Characteristic not found: CURRENT_TIME_CHARACTERISTIC_UUID");
}
}
private void writeCasioLocalTimeInformation(TransactionBuilder builder)
{
Calendar cal = Calendar.getInstance();
int zoneOffset = (int)TimeUnit.MILLISECONDS.toMinutes(cal.get(Calendar.ZONE_OFFSET));
int dstOffset = (int)TimeUnit.MILLISECONDS.toMinutes(cal.get(Calendar.DST_OFFSET));
byte byte0 = (byte)(zoneOffset / 15);
byte byte1 = (byte)(dstOffset / 15);
BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.LOCAL_TIME_CHARACTERISTIC_UUID);
if(charact != null) {
builder.write(charact, new byte[]{byte0, byte1});
}
else {
LOG.warn("Characteristic not found: LOCAL_TIME_CHARACTERISTIC_UUID");
}
}
private void writeCasioVirtualServerFeature(TransactionBuilder builder)
{
byte byte0 = (byte)0;
byte0 |= 1; // Casio Current Time Service
byte0 |= 2; // Casio Alert Notification Service
byte0 |= 4; // Casio Phone Alert Status Service
byte0 |= 8; // Casio Immediate Alert Service
BluetoothGattCharacteristic charact = getCharacteristic(CasioGB6900Constants.CASIO_VIRTUAL_SERVER_FEATURES);
if(charact != null) {
builder.write(charact, new byte[]{byte0, 0x00});
}
else {
LOG.warn("Characteristic not found: CASIO_VIRTUAL_SERVER_FEATURES");
}
}
private boolean handleCasioCom(byte[] data)
{
boolean handled = false;
switch(data[0]) // ServiceID - actually an int
{
case 0:
switch(data[2])
{
case (byte) 1:
LOG.info("Initialization done, setting state to INITIALIZED");
gbDevice.setState(GBDevice.State.INITIALIZED);
gbDevice.sendDeviceUpdateIntent(getContext());
break;
}
break;
case 2:
switch(data[2]) // Request Type
{
case (byte) 1:
try
{
TransactionBuilder builder = createTransactionBuilder("writeCasioCurrentTime");
writeCasioCurrentTime(builder);
performImmediately(builder);
handled = true;
} catch (IOException e) {
LOG.warn(e.getMessage());
}
break;
case (byte) 2:
try
{
TransactionBuilder builder = createTransactionBuilder("writeCasioLocalTimeInformation");
writeCasioLocalTimeInformation(builder);
performImmediately(builder);
handled = true;
} catch (IOException e) {
LOG.warn(e.getMessage());
}
break;
}
break;
case 7:
try
{
TransactionBuilder builder = createTransactionBuilder("writeCasioVirtualServerFeature");
writeCasioVirtualServerFeature(builder);
performImmediately(builder);
handled = true;
} catch (IOException e) {
LOG.warn(e.getMessage());
}
break;
}
return handled;
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
boolean handled = false;
if (super.onCharacteristicChanged(gatt, characteristic)) {
return true;
}
UUID characteristicUUID = characteristic.getUuid();
byte[] data = characteristic.getValue();
if (data.length == 0)
return true;
if(characteristicUUID.equals(CasioGB6900Constants.CASIO_A_NOT_W_REQ_NOT))
{
handled = handleCasioCom(data);
}
if(characteristicUUID.equals(CasioGB6900Constants.CASIO_A_NOT_COM_SET_NOT))
{
handled = handleCasioCom(data);
}
if(characteristicUUID.equals(CasioGB6900Constants.ALERT_LEVEL_CHARACTERISTIC_UUID))
{
GBDeviceEventFindPhone findPhoneEvent = new GBDeviceEventFindPhone();
if(data[0] == 0x02) {
findPhoneEvent.event = GBDeviceEventFindPhone.Event.START;
}
else
{
findPhoneEvent.event = GBDeviceEventFindPhone.Event.STOP;
}
evaluateGBDeviceEvent(findPhoneEvent);
handled = true;
}
if(characteristicUUID.equals(CasioGB6900Constants.RINGER_CONTROL_POINT))
{
if(data[0] == 0x02)
{
LOG.info("Mute/ignore call event not yet supported by GB");
}
handled = true;
}
if(!handled)
{
LOG.info("Unhandled characteristic change: " + characteristicUUID + " code: " + String.format("0x%1x ...", data[0]));
}
return true;
}
private void showNotification(byte icon, String title, String message) {
try {
TransactionBuilder builder = performInitialized("showNotification");
int len;
byte[] titleBytes = title.getBytes(StandardCharsets.US_ASCII);
len = titleBytes.length > 18 ? 18 : titleBytes.length;
byte[] msg = new byte[2 + len];
msg[0] = icon;
msg[1] = 1;
for(int i=0; i<len; i++)
{
msg[i + 2] = titleBytes[i];
}
builder.write(getCharacteristic(CasioGB6900Constants.ALERT_CHARACTERISTIC_UUID), msg);
LOG.info("Showing notification, title: " + title + " message (not sent): " + message);
performConnected(builder.getTransaction());
} catch (IOException e) {
LOG.warn(e.getMessage());
}
}
@Override
public boolean useAutoConnect() {
return true;
}
@Override
public void onNotification(NotificationSpec notificationSpec) {
String notificationTitle = StringUtils.getFirstOf(notificationSpec.sender, notificationSpec.title);
byte icon;
switch (notificationSpec.type) {
case GENERIC_SMS:
icon = CasioGB6900Constants.SMS_NOTIFICATION_ID;
break;
case GENERIC_CALENDAR:
icon = CasioGB6900Constants.CALENDAR_NOTIFICATION_ID;
break;
case GENERIC_EMAIL:
icon = CasioGB6900Constants.MAIL_NOTIFICATION_ID;
break;
default:
icon = CasioGB6900Constants.SNS_NOTIFICATION_ID;
break;
}
showNotification(icon, notificationTitle, notificationSpec.body);
}
@Override
public void onDeleteNotification(int id) {
}
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
}
@Override
public void onSetTime() {
try {
TransactionBuilder builder = performInitialized("SetTime");
writeCasioLocalTimeInformation(builder);
writeCasioCurrentTime(builder);
performConnected(builder.getTransaction());
} catch(IOException e) {
LOG.warn(e.getMessage());
}
}
@Override
public void onSetCallState(CallSpec callSpec) {
switch (callSpec.command) {
case CallSpec.CALL_INCOMING:
showNotification(CasioGB6900Constants.CALL_NOTIFICATION_ID, callSpec.name, callSpec.number);
break;
}
}
@Override
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
}
@Override
public void onSetMusicState(MusicStateSpec stateSpec) {
}
@Override
public void onSetMusicInfo(MusicSpec musicSpec) {
}
@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 {
} catch(Exception e) {
LOG.warn(e.getMessage());
}
}
@Override
public void onHeartRateTest() {
}
@Override
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
}
@Override
public void onFindDevice(boolean start) {
if(start) {
showNotification(CasioGB6900Constants.SNS_NOTIFICATION_ID, "You found it!", "");
}
}
@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 onTestNewFunction() {
}
@Override
public void onSendWeather(WeatherSpec weatherSpec) {
}
}

View File

@ -41,6 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.UnknownDeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.casiogb6900.CasioGB6900DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.EXRIZUK8Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.HPlusCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.hplus.MakibesF68Coordinator;
@ -216,6 +217,7 @@ public class DeviceHelper {
result.add(new Watch9DeviceCoordinator());
result.add(new Roidmi1Coordinator());
result.add(new Roidmi3Coordinator());
result.add(new CasioGB6900DeviceCoordinator());
return result;
}

View File

@ -628,6 +628,7 @@
<string name="devicetype_watch9">Watch 9</string>
<string name="devicetype_roidmi">Roidmi</string>
<string name="devicetype_roidmi3">Roidmi 3</string>
<string name="devicetype_casiogb6900">Casio GB-6900</string>
<string name="choose_auto_export_location">Choose export location</string>
<string name="notification_channel_name">Gadgetbridge notifications</string>