From 4a73d2254a87e0f280fc8c9f711331c6c48a160a Mon Sep 17 00:00:00 2001 From: Daniel Dakhno Date: Mon, 22 Apr 2024 22:36:28 +0200 Subject: [PATCH] Device Pavlok: added basic device files --- .../devices/pavlok/PavlokCoordinator.java | 42 ++++++++ .../gadgetbridge/model/DeviceType.java | 2 + .../service/devices/pavlok/PavlokSupport.java | 95 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 4 files changed, 140 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pavlok/PavlokCoordinator.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pavlok/PavlokSupport.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pavlok/PavlokCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pavlok/PavlokCoordinator.java new file mode 100644 index 000000000..248008739 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pavlok/PavlokCoordinator.java @@ -0,0 +1,42 @@ +package nodomain.freeyourgadget.gadgetbridge.devices.pavlok; + +import androidx.annotation.NonNull; + +import java.util.regex.Pattern; + +import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator; +import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; +import nodomain.freeyourgadget.gadgetbridge.entities.Device; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.devices.pavlok.PavlokSupport; + +public class PavlokCoordinator extends AbstractBLEDeviceCoordinator { + @Override + protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { + + } + + @Override + protected Pattern getSupportedDeviceName() { + return Pattern.compile("^Pavlok-.*"); + } + + @Override + public String getManufacturer() { + return "Pavlok"; + } + + @NonNull + @Override + public Class getDeviceSupportClass() { + return PavlokSupport.class; + } + + @Override + public int getDeviceNameResource() { + return R.string.devicetype_pavlok; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java index 5f8f2b2f8..f73854a9d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java @@ -145,6 +145,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.nothing.Ear1Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.nothing.Ear2Coordinator; import nodomain.freeyourgadget.gadgetbridge.devices.nothing.EarStickCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.nut.NutCoordinator; +import nodomain.freeyourgadget.gadgetbridge.devices.pavlok.PavlokCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeJFCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.qc35.QC35Coordinator; @@ -378,6 +379,7 @@ public enum DeviceType { FEMOMETER_VINCA2(FemometerVinca2DeviceCoordinator.class), PIXOO(PixooCoordinator.class), SCANNABLE(ScannableDeviceCoordinator.class), + PAVLOK(PavlokCoordinator.class), TEST(TestDeviceCoordinator.class); private DeviceCoordinator coordinator; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pavlok/PavlokSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pavlok/PavlokSupport.java new file mode 100644 index 000000000..0c39b5d68 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/pavlok/PavlokSupport.java @@ -0,0 +1,95 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.pavlok; + +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCharacteristic; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.UUID; + +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattCharacteristic; +import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService; +import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.ReadAction; +import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction; +import nodomain.freeyourgadget.gadgetbridge.service.devices.nut.NutSupport; + +public class PavlokSupport extends AbstractBTLEDeviceSupport { + private static final Logger LOG = LoggerFactory.getLogger(PavlokSupport.class); + + private static final String ACTION_ZAP = "nodomain.freeyourgadget.gadgetbridge.devices.pavlok.ACTION_ZAP"; + + private static final UUID UUID_PAVLOK_SERVICE = UUID.fromString("156e1000-a300-4fea-897b-86f698d74461"); + private static final UUID UUID_PAVLOK_CHARACTERISTIC_ZAP = UUID.fromString("00001003-0000-1000-8000-00805f9b34fb"); + + BroadcastReceiver zapReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int intensity = intent.getIntExtra("EXTRA_INTENSITY", 0); + if((intensity <= 0) || (intensity > 100)){ + LOG.error("EXTRA_INTENSITY missing, <= 0 or > 100"); + return; + } + + sendZap(intensity); + } + }; + + private void sendZap(int intensity){ + byte[] packet = new byte[]{(byte) 0x89, (byte) intensity}; + + new TransactionBuilder("send pavlok zap") + .write(getCharacteristic(UUID_PAVLOK_CHARACTERISTIC_ZAP), packet) + .queue(getQueue()); + } + + public PavlokSupport() { + super(LOG); + addSupportedService(GattService.UUID_SERVICE_BATTERY_SERVICE); + addSupportedService(UUID_PAVLOK_SERVICE); + } + + @Override + public void dispose() { + getContext().unregisterReceiver(zapReceiver); + super.dispose(); + } + + @Override + public boolean useAutoConnect() { + return false; + } + + @Override + protected TransactionBuilder initializeDevice(TransactionBuilder builder) { + getContext().registerReceiver( + zapReceiver, + new IntentFilter(ACTION_ZAP), Context.RECEIVER_EXPORTED + ); + + return builder + .add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext())) + .add(new ReadAction(getCharacteristic(GattCharacteristic.UUID_CHARACTERISTIC_BATTERY_LEVEL))) + .add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext())); + } + + @Override + public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { + if(characteristic.getUuid().equals(GattCharacteristic.UUID_CHARACTERISTIC_BATTERY_LEVEL)){ + int level = (int) characteristic.getValue()[0]; + getDevice().setBatteryLevel(level); + getDevice().sendDeviceUpdateIntent(getContext()); + } + + return super.onCharacteristicRead(gatt, characteristic, status); + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f907d1705..bee877faa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2796,4 +2796,5 @@ Switch between main screens only using horizontal swiping Midnight at bottom In 24h mode, draw midnight at the bottom, midday at the top of the chart + Pavlok shocker