1
0
mirror of https://github.com/oliexdev/openScale.git synced 2025-08-09 18:26:38 +02:00

added support for Medisana BS 444 Connect scale

set default device name on Bluetooth type change
This commit is contained in:
OliE
2017-06-04 16:03:05 +02:00
parent 6c6737f424
commit 29e67659a5
7 changed files with 287 additions and 11 deletions

View File

@@ -26,6 +26,7 @@ import android.util.Log;
import com.health.openscale.core.alarm.AlarmHandler;
import com.health.openscale.core.bluetooth.BluetoothCommunication;
import com.health.openscale.core.bluetooth.BluetoothCustomOpenScale;
import com.health.openscale.core.bluetooth.BluetoothMedisanaBS444;
import com.health.openscale.core.bluetooth.BluetoothMiScale;
import com.health.openscale.core.bluetooth.BluetoothSanitasSbf70;
import com.health.openscale.core.database.ScaleDatabase;
@@ -47,6 +48,7 @@ import java.util.ArrayList;
import java.util.Map;
import java.util.TreeMap;
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_MEDISANA_BS444;
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_MI_SCALE;
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_OPEN_SCALE;
import static com.health.openscale.core.bluetooth.BluetoothCommunication.BT_SANITAS_SBF70;
@@ -345,6 +347,9 @@ public class OpenScale {
case BT_SANITAS_SBF70:
btCom = new BluetoothSanitasSbf70(context);
break;
case BT_MEDISANA_BS444:
btCom = new BluetoothMedisanaBS444(context);
break;
default:
Log.e("OpenScale", "No valid scale type selected");
return;

View File

@@ -23,6 +23,7 @@ public abstract class BluetoothCommunication {
public static final int BT_MI_SCALE = 0;
public static final int BT_OPEN_SCALE = 1;
public static final int BT_SANITAS_SBF70 = 2;
public static final int BT_MEDISANA_BS444 = 3;
public static final int BT_RETRIEVE_SCALE_DATA = 0;
public static final int BT_INIT_PROCESS = 1;

View File

@@ -0,0 +1,250 @@
/* Copyright (C) 2014 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.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.health.openscale.core.datatypes.ScaleData;
import java.util.Date;
import java.util.UUID;
public class BluetoothMedisanaBS444 extends BluetoothCommunication {
private BluetoothGatt bluetoothGatt;
private BluetoothAdapter.LeScanCallback scanCallback;
private final UUID WEIGHT_MEASUREMENT_SERVICE = UUID.fromString("000078b2-0000-1000-8000-00805f9b34fb");
private final UUID WEIGHT_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00008a21-0000-1000-8000-00805f9b34fb"); // indication, read-only
private final UUID FEATURE_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00008a22-0000-1000-8000-00805f9b34fb"); // indication, read-only
private final UUID CUSTOM3_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00008a20-0000-1000-8000-00805f9b34fb"); // read-only
private final UUID CMD_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00008a81-0000-1000-8000-00805f9b34fb"); // write-only
private final UUID CUSTOM5_MEASUREMENT_CHARACTERISTIC = UUID.fromString("00008a82-0000-1000-8000-00805f9b34fb"); // indication, read-only
private final UUID WEIGHT_MEASUREMENT_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private String btDeviceName;
private Handler searchHandler;
private Context context;
private ScaleData btScaleData;
private int nextCmdState;
public BluetoothMedisanaBS444(Context context) {
this.context = context;
searchHandler = new Handler();
btScaleData = new ScaleData();
scanCallback = null;
}
@Override
public void startSearching(String deviceName) {
btDeviceName = deviceName;
if (scanCallback == null) {
scanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
if (device.getAddress().replace(":", "").toUpperCase().startsWith("E454EB")) {
//if (device.getName().equals(btDeviceName)) {
bluetoothGatt = device.connectGatt(context, false, gattCallback);
searchHandler.removeCallbacksAndMessages(null);
btAdapter.stopLeScan(scanCallback);
//}
}
}
};
}
searchHandler.postDelayed(new Runnable()
{
@Override
public void run()
{
btAdapter.stopLeScan(scanCallback);
callbackBtHandler.obtainMessage(BluetoothCommunication.BT_NO_DEVICE_FOUND).sendToTarget();
}
}, 10000);
btAdapter.startLeScan(scanCallback);
}
@Override
public void stopSearching() {
if (bluetoothGatt != null)
{
bluetoothGatt.close();
bluetoothGatt = null;
}
searchHandler.removeCallbacksAndMessages(null);
btAdapter.stopLeScan(scanCallback);
}
private BluetoothGattCallback gattCallback= new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
callbackBtHandler.obtainMessage(BluetoothCommunication.BT_CONNECTION_ESTABLISHED).sendToTarget();
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
callbackBtHandler.obtainMessage(BluetoothCommunication.BT_CONNECTION_LOST).sendToTarget();
stopSearching();
}
}
@Override
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
nextCmdState = 0;
invokeNextBluetoothCmd(gatt);
}
private void invokeNextBluetoothCmd(BluetoothGatt gatt) {
BluetoothGattCharacteristic characteristic;
BluetoothGattDescriptor descriptor;
switch (nextCmdState) {
case 0:
// set indication on for feature characteristic
characteristic = gatt.getService(WEIGHT_MEASUREMENT_SERVICE)
.getCharacteristic(FEATURE_MEASUREMENT_CHARACTERISTIC);
gatt.setCharacteristicNotification(characteristic, true);
descriptor = characteristic.getDescriptor(WEIGHT_MEASUREMENT_CONFIG);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
break;
case 1:
// set indication on for weight measurement
characteristic = gatt.getService(WEIGHT_MEASUREMENT_SERVICE)
.getCharacteristic(WEIGHT_MEASUREMENT_CHARACTERISTIC);
gatt.setCharacteristicNotification(characteristic, true);
descriptor = characteristic.getDescriptor(WEIGHT_MEASUREMENT_CONFIG);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
break;
case 2:
// set indication on for custom5 measurement
characteristic = gatt.getService(WEIGHT_MEASUREMENT_SERVICE)
.getCharacteristic(CUSTOM5_MEASUREMENT_CHARACTERISTIC);
gatt.setCharacteristicNotification(characteristic, true);
descriptor = characteristic.getDescriptor(WEIGHT_MEASUREMENT_CONFIG);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
break;
case 3:
// send magic number to receive weight data
characteristic = gatt.getService(WEIGHT_MEASUREMENT_SERVICE)
.getCharacteristic(CMD_MEASUREMENT_CHARACTERISTIC);
characteristic.setValue(new byte[]{(byte)0x02, (byte)0x7B, (byte)0x7B, (byte)0xF6, (byte)0x0D}); // 02:7b:7b:f6:0d
gatt.writeCharacteristic(characteristic);
break;
case 4:
// end of command mode
break;
default:
Log.e("BluetoothMedisanaScale", "Error invalid Bluetooth State");
break;
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor,
int status) {
nextCmdState++;
invokeNextBluetoothCmd(gatt);
}
@Override
public void onCharacteristicWrite (BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
nextCmdState++;
invokeNextBluetoothCmd(gatt);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
final byte[] data = characteristic.getValue();
if (characteristic.getUuid().equals(WEIGHT_MEASUREMENT_CHARACTERISTIC)) {
parseWeightData(data);
}
if (characteristic.getUuid().equals(FEATURE_MEASUREMENT_CHARACTERISTIC)) {
parseFeatureData(data);
callbackBtHandler.obtainMessage(BluetoothCommunication.BT_RETRIEVE_SCALE_DATA, btScaleData).sendToTarget();
}
}
};
private void parseWeightData(byte[] weightData) {
float weight = (float)(((weightData[2] & 0xFF) << 8) | (weightData[1] & 0xFF)) / 100.0f;
long unix_timestamp = ((weightData[8] & 0xFF) << 24) | ((weightData[7] & 0xFF) << 16) | ((weightData[6] & 0xFF) << 8) | (weightData[5] & 0xFF); // elapsed time in seconds since 2010
Date btDate = new Date();
unix_timestamp += 1262304000; // +40 years because unix time starts in year 1970
btDate.setTime(unix_timestamp*1000); // multiply with 1000 to get milliseconds
btScaleData.setDateTime(btDate);
btScaleData.setWeight(weight);
}
private void parseFeatureData(byte[] featureData) {
//btScaleData.setKCal(((featureData[7] & 0xFF) << 8) | (featureData[6] & 0xFF));
btScaleData.setFat(decodeFeature(featureData[8], featureData[9]));
btScaleData.setWater(decodeFeature(featureData[10], featureData[11]));
btScaleData.setMuscle(decodeFeature(featureData[12], featureData[13]));
//btScaleData.setBone(decodeFeature(featureData[14], featureData[15]));
}
private float decodeFeature(byte highByte, byte lowByte) {
return (float)(((lowByte& 0x0F) << 8) | (highByte & 0xFF)) / 10.0f;
}
private void printByteInHex(byte[] data) {
if (data == null) {
Log.e("BluetoothMedisanaScale", "Data is null");
return;
}
final StringBuilder stringBuilder = new StringBuilder(data.length);
for(byte byteChar : data) {
stringBuilder.append(String.format("%02X ", byteChar));
}
Log.d("BluetoothMedisanaScale", "Raw hex data: " + stringBuilder);
}
}

View File

@@ -385,8 +385,9 @@ public class BluetoothSanitasSbf70 extends BluetoothCommunication {
+ device.getAddress() + " : " + device.getName()
);
// Texas Instrument (Sanitas)
if (!device.getAddress().replace(":", "").toUpperCase().startsWith("C4BE84"))
return;
if (device.getAddress().replace(":", "").toUpperCase().startsWith("C4BE84") ||
device.getAddress().replace(":", "").toUpperCase().startsWith("209148")) {
if (!device.getName().toLowerCase().equals(btDeviceName.toLowerCase()))
return;
Log.d(TAG, "Sanitas scale found. Connecting...");
@@ -396,6 +397,7 @@ public class BluetoothSanitasSbf70 extends BluetoothCommunication {
searchHandler.removeCallbacksAndMessages(null);
btAdapter.stopLeScan(scanCallback);
}
}
};
}

View File

@@ -187,8 +187,7 @@ public class MainActivity extends ActionBarActivity implements
String deviceType = prefs.getString("btDeviceTypes", "0");
// Check if Bluetooth 4.x is available
if (Integer.parseInt(deviceType) == BluetoothCommunication.BT_MI_SCALE
|| Integer.parseInt(deviceType) == BluetoothCommunication.BT_SANITAS_SBF70) {
if (Integer.parseInt(deviceType) != BluetoothCommunication.BT_OPEN_SCALE) {
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
setBluetoothStatusIcon(R.drawable.bluetooth_disabled);
Toast.makeText(getApplicationContext(), "Bluetooth 4.x " + getResources().getString(R.string.info_is_not_available), Toast.LENGTH_SHORT).show();

View File

@@ -36,6 +36,7 @@ public class BluetoothPreferences extends PreferenceFragment implements SharedPr
String[] btDeviceSupportInit;
String[] btDeviceSupportDataTransfer;
String[] btDeviceSupportDataHistory;
String[] btDeviceDefaultName;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -46,6 +47,7 @@ public class BluetoothPreferences extends PreferenceFragment implements SharedPr
btDeviceSupportInit = getResources().getStringArray(R.array.bt_device_support_initializing);
btDeviceSupportDataTransfer = getResources().getStringArray(R.array.bt_device_support_data_transfer);
btDeviceSupportDataHistory = getResources().getStringArray(R.array.bt_device_support_data_history);
btDeviceDefaultName = getResources().getStringArray(R.array.bt_device_default_name);
initSummary(getPreferenceScreen());
}
@@ -91,6 +93,11 @@ public class BluetoothPreferences extends PreferenceFragment implements SharedPr
getResources().getString(R.string.label_bt_device_data_transfer) + ": " + btDeviceSupportDataTransfer[i] + "<br>" +
getResources().getString(R.string.label_bt_device_data_history) + ": " + btDeviceSupportDataHistory[i]
));
getPreferenceManager().getDefaultSharedPreferences(getActivity().getApplicationContext()).edit().putString("btDeviceName", btDeviceDefaultName[i]).commit();
EditTextPreference prefDeviceName = (EditTextPreference)findPreference("btDeviceName");
prefDeviceName.setSummary(btDeviceDefaultName[i]);
prefDeviceName.setText(btDeviceDefaultName[i]);
}
if (p instanceof EditTextPreference) {

View File

@@ -3,31 +3,43 @@
<string-array name="bt_device_entries">
<item>Xiaomi Mi Scale v1</item>
<item>openScale Custom Scale</item>
<item>Sanitas SBF-70</item>
<item>Sanitas SBF-70</item>#
<item>Medisana BS444 Connect</item>
</string-array>
<string-array name="bt_device_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
<string-array name="bt_device_default_name">
<item>MI_SCALE</item>
<item>openScale_MCU</item>
<item>SANITAS SBF70</item>
<item>Medisana BS444</item>
</string-array>
<string-array name="bt_device_support_initializing">
<item>@string/label_yes</item>
<item>@string/label_yes</item>
<item>@string/label_no</item>
<item>@string/label_no</item>
</string-array>
<string-array name="bt_device_support_data_transfer">
<item>@string/label_yes</item>
<item>@string/label_yes</item>
<item>@string/label_yes</item>
<item>@string/label_yes</item>
</string-array>
<string-array name="bt_device_support_data_history">
<item>@string/label_yes</item>
<item>@string/label_yes</item>
<item>@string/label_no</item>
<item>@string/label_yes</item>
</string-array>
</resources>