Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReOpen PR Support for nameless OKOK scales (Myria MY4836) #1088

Merged
merged 2 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public static BluetoothCommunication createDeviceDriver(Context context, String
if (name.equals("Health Scale".toLowerCase(Locale.US))) {
return new BluetoothOneByone(context);
}
if(name.equals("1byone scale".toLowerCase(Locale.US))){
if(name.equals("1byone scale".toLowerCase(Locale.US))) {
return new BluetoothOneByoneNew(context);
}

Expand Down Expand Up @@ -114,10 +114,13 @@ public static BluetoothCommunication createDeviceDriver(Context context, String
}
if (deviceName.equals("Hoffen BS-8107")) {
return new BluetoothHoffenBBS8107(context);
}
}
if (deviceName.equals("ADV") || deviceName.equals("Chipsea-BLE")) {
return new BluetoothOKOK(context);
}
if (deviceName.isEmpty()) {
return new BluetoothOKOK2(context);
}
if (deviceName.equals("BF105") || deviceName.equals("BF720")) {
return new BluetoothBeurerBF105(context);
}
Expand All @@ -130,10 +133,10 @@ public static BluetoothCommunication createDeviceDriver(Context context, String
if (deviceName.equals("SBF72") || deviceName.equals("BF915") || deviceName.equals("SBF73")) {
return new BluetoothSanitasSBF72(context, deviceName);
}
if (deviceName.equals("Weight Scale")){
if (deviceName.equals("Weight Scale")) {
return new BluetoothSinocare(context);
}
if (deviceName.equals("CH100")){
if (deviceName.equals("CH100")) {
return new BluetoothHuaweiAH100(context);
}
if (deviceName.equals("ES-26BB-B")){
Expand All @@ -142,7 +145,7 @@ public static BluetoothCommunication createDeviceDriver(Context context, String
if (deviceName.equals("Yoda1")){
return new BluetoothYoda1Scale(context);
}
if (deviceName.equals("AAA002") || deviceName.equals("AAA007")){
if (deviceName.equals("AAA002") || deviceName.equals("AAA007")) {
return new BluetoothBroadcastScale(context);
}
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/* Copyright (C) 2024 olie.xdev <olie.xdev@googlemail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.health.openscale.core.bluetooth;

import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanResult;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.SparseArray;

import com.health.openscale.core.datatypes.ScaleMeasurement;
import com.health.openscale.core.utils.Converters;
import com.welie.blessed.BluetoothCentralManager;
import com.welie.blessed.BluetoothCentralManagerCallback;
import com.welie.blessed.BluetoothPeripheral;

import org.jetbrains.annotations.NotNull;

import java.util.LinkedList;
import java.util.List;

import timber.log.Timber;

import static com.health.openscale.core.utils.Converters.WeightUnit.LB;
import static com.health.openscale.core.utils.Converters.WeightUnit.ST;

public class BluetoothOKOK2 extends BluetoothCommunication {
private static final int IDX_WEIGHT_MSB = 0;
private static final int IDX_WEIGHT_LSB = 1;
private static final int IDX_IMPEDANCE_MSB = 2;
private static final int IDX_IMPEDANCE_LSB = 3;
private static final int IDX_PRODUCTID_MSB = 4;
private static final int IDX_PRODUCTID_LSB = 5;
private static final int IDX_ATTRIB = 6;
private static final int IDX_MAC_1 = 7;
private static final int IDX_MAC_2 = 8;
private static final int IDX_MAC_3 = 9;
private static final int IDX_MAC_4 = 10;
private static final int IDX_MAC_5 = 11;
private static final int IDX_MAC_6 = 12;

private static final int UNIT_KG = 0;
private static final int UNIT_LB = 2;
private static final int UNIT_STLB = 3;

private BluetoothCentralManager central;
private String mMacAddress;
private float mLastWeight = 0f;

public BluetoothOKOK2(Context context) {
super(context);
central = new BluetoothCentralManager(context, btCallback, new Handler(Looper.getMainLooper()));
}

private final BluetoothCentralManagerCallback btCallback = new BluetoothCentralManagerCallback() {
@Override
public void onDiscoveredPeripheral(@NotNull BluetoothPeripheral peripheral, @NotNull ScanResult scanResult) {
SparseArray<byte[]> manufacturerSpecificData = scanResult.getScanRecord().getManufacturerSpecificData();
int vendorIndex = -1;
for (int i = 0; i < manufacturerSpecificData.size(); i++) {
int vendorId = manufacturerSpecificData.keyAt(i);
if ((vendorId & 0xff) == 0xc0) { // 0x00c0-->0xffc0
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the 0xc0 the identifier for the scale?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ID is 0x??c0 (0x00c0, 0x01c0, 0x02c0.... ) where the MSB is used to notify the progress, it starts from 0 and increases until the measurement is stable

vendorIndex = vendorId;
break;
}
}
if (vendorIndex == -1) {
return;
}
byte[] data = manufacturerSpecificData.get(vendorIndex);

StringBuilder sb = new StringBuilder(data.length * 3);
for (byte b : data) {
sb.append(String.format("%02x ", b));
}
Timber.d("manufacturerSpecificData: [VID=%04x] %s", vendorIndex, sb.toString());

if (data[IDX_MAC_1] != (byte) ((Character.digit(mMacAddress.charAt(0), 16) << 4) + Character.digit(mMacAddress.charAt(1), 16))
|| data[IDX_MAC_2] != (byte) ((Character.digit(mMacAddress.charAt(3), 16) << 4) + Character.digit(mMacAddress.charAt(4), 16))
|| data[IDX_MAC_3] != (byte) ((Character.digit(mMacAddress.charAt(6), 16) << 4) + Character.digit(mMacAddress.charAt(7), 16))
|| data[IDX_MAC_4] != (byte) ((Character.digit(mMacAddress.charAt(9), 16) << 4) + Character.digit(mMacAddress.charAt(10), 16))
|| data[IDX_MAC_5] != (byte) ((Character.digit(mMacAddress.charAt(12), 16) << 4) + Character.digit(mMacAddress.charAt(13), 16))
|| data[IDX_MAC_6] != (byte) ((Character.digit(mMacAddress.charAt(15), 16) << 4) + Character.digit(mMacAddress.charAt(16), 16)))
return;

if ((data[IDX_ATTRIB] & 1) == 0) // in progress
return;

float divider = 10f;
switch ((data[IDX_ATTRIB] >> 1) & 3) {
case 0:
divider = 10f;
break;
case 1:
divider = 1f;
break;
case 2:
divider = 100f;
break;
}

float weight = 0f;
switch ((data[IDX_ATTRIB] >> 3) & 3) {
case UNIT_KG: {
float val = ((data[IDX_WEIGHT_MSB] & 0xff) << 8) | (data[IDX_WEIGHT_LSB] & 0xff);
weight = val / divider;
break;
}
case UNIT_LB: {
float val = ((data[IDX_WEIGHT_MSB] & 0xff) << 8) | (data[IDX_WEIGHT_LSB] & 0xff);
weight = Converters.toKilogram(val / divider, LB);
break;
}
case UNIT_STLB: {
float val = data[IDX_WEIGHT_MSB] /*ST*/ + data[IDX_WEIGHT_LSB] /*LB*/ / divider / 14f;
weight = Converters.toKilogram(val, ST);
break;
}
}

if (mLastWeight != weight) {
ScaleMeasurement entry = new ScaleMeasurement();
entry.setWeight(weight);
addScaleMeasurement(entry);
mLastWeight = weight;
// disconnect();
}
}
};

@Override
public void connect(String macAddress) {
mMacAddress = macAddress;
List<ScanFilter> filters = new LinkedList<>();

byte[] data = new byte[13];
data[IDX_MAC_1] = (byte) ((Character.digit(macAddress.charAt(0), 16) << 4) + Character.digit(macAddress.charAt(1), 16));
data[IDX_MAC_2] = (byte) ((Character.digit(macAddress.charAt(3), 16) << 4) + Character.digit(macAddress.charAt(4), 16));
data[IDX_MAC_3] = (byte) ((Character.digit(macAddress.charAt(6), 16) << 4) + Character.digit(macAddress.charAt(7), 16));
data[IDX_MAC_4] = (byte) ((Character.digit(macAddress.charAt(9), 16) << 4) + Character.digit(macAddress.charAt(10), 16));
data[IDX_MAC_5] = (byte) ((Character.digit(macAddress.charAt(12), 16) << 4) + Character.digit(macAddress.charAt(13), 16));
data[IDX_MAC_6] = (byte) ((Character.digit(macAddress.charAt(15), 16) << 4) + Character.digit(macAddress.charAt(16), 16));
byte[] mask = new byte[13];
mask[IDX_MAC_1] = mask[IDX_MAC_2] = mask[IDX_MAC_3] = mask[IDX_MAC_4] = mask[IDX_MAC_5] = mask[IDX_MAC_6] = (byte) 0xff;

// TODO: verify setAdvertisingDataTypeWithData on API33+
// b.setAdvertisingDataTypeWithData(ScanRecord.DATA_TYPE_MANUFACTURER_SPECIFIC_DATA, data, mask);
for (int i = 0x00; i <= 0xff; i++) {
ScanFilter.Builder b = new ScanFilter.Builder();
b.setDeviceAddress(macAddress);
b.setManufacturerData((i << 8) | 0xc0, data, mask);
filters.add(b.build());
}

central.scanForPeripheralsUsingFilters(filters);
}

@Override
public void disconnect() {
if (central != null)
central.stopScan();
central = null;
super.disconnect();
}

@Override
public String driverName() {
return "OKOK (nameless)";
}

@Override
protected boolean onNextStep(int stepNr) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
Expand All @@ -41,6 +42,9 @@ public class BluetoothPreferences extends PreferenceFragmentCompat {
private Preference btScanner;

private static final String formatDeviceName(String name, String address) {
if (TextUtils.isEmpty(name) && !address.isEmpty()) {
return String.format("[%s]", address);
}
if (name.isEmpty() || address.isEmpty()) {
return "-";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.view.Gravity;
Expand Down Expand Up @@ -228,6 +229,9 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
}

private static final String formatDeviceName(String name, String address) {
if (TextUtils.isEmpty(name) && !address.isEmpty()) {
return String.format("[%s]", address);
}
if (name.isEmpty() || address.isEmpty()) {
return "-";
}
Expand Down Expand Up @@ -314,14 +318,15 @@ private void onDeviceFound(final ScanResult bleScanResult) {
BluetoothDevice device = bleScanResult.getDevice();
Context context = getContext();

if (device.getName() == null || foundDevices.containsKey(device.getAddress()) || context == null) {
if (foundDevices.containsKey(device.getAddress()) || context == null) {
return;
}

BluetoothDeviceView deviceView = new BluetoothDeviceView(context);
deviceView.setDeviceName(formatDeviceName(bleScanResult.getDevice()));

BluetoothCommunication btDevice = BluetoothFactory.createDeviceDriver(context, device.getName());
String name = device.getName() != null ? device.getName() : "";
BluetoothCommunication btDevice = BluetoothFactory.createDeviceDriver(context, name);
if (btDevice != null) {
Timber.d("Found supported device %s (driver: %s)",
formatDeviceName(device), btDevice.driverName());
Expand Down