mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-28 12:56:49 +01:00
Add initial support for Casio GB-6900B
This commit is contained in:
parent
5b4fd32ac1
commit
2c2d07067d
@ -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;
|
||||
|
||||
|
||||
}
|
@ -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 {
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user