use of com.health.openscale.core.datatypes.ScaleUser in project openScale by oliexdev.
the class DatabaseTest method userOperations.
@Test
public void userOperations() throws Exception {
ScaleUser user1 = new ScaleUser();
ScaleUser user2 = new ScaleUser();
user1.setUserName("foo");
user2.setUserName("bar");
// is user database empty on initialization
assertTrue(userDao.getAll().isEmpty());
userDao.insert(user1);
// was the user successfully inserted
assertEquals(1, userDao.getAll().size());
assertEquals("foo", userDao.getAll().get(0).getUserName());
userDao.insert(user2);
assertEquals(2, userDao.getAll().size());
assertEquals("foo", userDao.getAll().get(0).getUserName());
assertEquals("bar", userDao.getAll().get(1).getUserName());
// check if get(id) works
List<ScaleUser> scaleUserList = userDao.getAll();
ScaleUser firstUser = scaleUserList.get(0);
ScaleUser secondUser = scaleUserList.get(1);
assertEquals(firstUser.getUserName(), userDao.get(firstUser.getId()).getUserName());
// check delete method
userDao.delete(firstUser);
assertEquals(1, userDao.getAll().size());
assertEquals(secondUser.getUserName(), userDao.getAll().get(0).getUserName());
// check update method
secondUser.setUserName("foobar");
userDao.update(secondUser);
assertEquals("foobar", userDao.get(secondUser.getId()).getUserName());
// clear database
userDao.delete(secondUser);
assertTrue(userDao.getAll().isEmpty());
// check insert user list
ScaleUser user3 = new ScaleUser();
user3.setUserName("bob");
List<ScaleUser> myScaleUserList = new ArrayList<>();
myScaleUserList.add(user1);
myScaleUserList.add(user2);
myScaleUserList.add(user3);
userDao.insertAll(myScaleUserList);
assertEquals(3, userDao.getAll().size());
}
use of com.health.openscale.core.datatypes.ScaleUser in project openScale by oliexdev.
the class BluetoothBeurerSanitas method onNextStep.
@Override
protected boolean onNextStep(int stepNr) {
switch(stepNr) {
case 0:
// Fresh start, so reset everything
measurementData = null;
storedMeasurement.measurementData = null;
readyForData = false;
dataReceived = false;
// Setup notification
setNotificationOn(CUSTOM_SERVICE_1, CUSTOM_CHARACTERISTIC_WEIGHT);
break;
case 1:
// we will be waiting for data in state 1
waitForDataInStep = 1;
// Say "Hello" to the scale and wait for ack
Timber.d("Sending command: ID_START_NIBBLE_INIT");
sendAlternativeStartCode(ID_START_NIBBLE_INIT, (byte) 0x01);
stopMachineState();
break;
case 2:
// Update time on the scale (no ack)
long unixTime = System.currentTimeMillis() / 1000L;
Timber.d("Sending command: ID_START_NIBBLE_SET_TIME");
sendAlternativeStartCode(ID_START_NIBBLE_SET_TIME, Converters.toInt32Be(unixTime));
break;
case 3:
// We will be waiting for data in state 3
waitForDataInStep = 3;
// Request scale status and wait for ack
Timber.d("Sending command: CMD_SCALE_STATUS");
sendCommand(CMD_SCALE_STATUS, encodeUserId(null));
stopMachineState();
break;
case 4:
// We will be waiting for data in state 4
waitForDataInStep = 4;
// Request list of all users and wait until all have been received
Timber.d("Sending command: CMD_USER_LIST");
sendCommand(CMD_USER_LIST);
stopMachineState();
break;
case 5:
// If currentRemoteUser is null, indexOf returns -1 and index will be 0
int index = remoteUsers.indexOf(currentRemoteUser) + 1;
currentRemoteUser = null;
// Find the next remote user that exists locally
for (; index < remoteUsers.size(); ++index) {
if (remoteUsers.get(index).localUserId != -1) {
currentRemoteUser = remoteUsers.get(index);
break;
}
}
// Fetch saved measurements
if (currentRemoteUser != null) {
// We will be waiting for data in state 5
waitForDataInStep = 5;
Timber.d("Request saved measurements (CMD_GET_SAVED_MEASUREMENTS) for %s", currentRemoteUser.name);
sendCommand(CMD_GET_SAVED_MEASUREMENTS, encodeUserId(currentRemoteUser));
stopMachineState();
}
// No user found, just continue to next step.
break;
case 6:
// Create a remote user for selected openScale user if needed
currentRemoteUser = null;
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
for (RemoteUser remoteUser : remoteUsers) {
if (remoteUser.localUserId == selectedUser.getId()) {
currentRemoteUser = remoteUser;
break;
}
}
if (currentRemoteUser == null) {
waitForDataInStep = 6;
createRemoteUser(selectedUser);
stopMachineState();
}
// Do not need to create new user, just continue to next step.
break;
case 7:
waitForDataInStep = 7;
Timber.d("Sending command: CMD_USER_DETAILS");
sendCommand(CMD_USER_DETAILS, encodeUserId(currentRemoteUser));
stopMachineState();
break;
case 8:
// If we have unprocessed data available, store it now.
if (storedMeasurement.measurementData != null) {
Timber.d("Reached state 8 (end) and still have saved data available. Storing now.");
if (currentRemoteUser != null) {
Timber.i("User has been identified in the meantime, so store the data for them.");
addMeasurement(measurementData, currentRemoteUser.localUserId);
} else {
Timber.i("User still not identified, so storing the data for the selected user.");
addMeasurement(measurementData, OpenScale.getInstance().getSelectedScaleUser().getId());
}
storedMeasurement.measurementData = null;
} else if (!dataReceived && currentRemoteUser != null && !currentRemoteUser.isNew) {
// Looks like we never received a fresh measurement in this run, so request it now.
// Chances are not good that this will work, but let's try it anyway.
waitForDataInStep = 8;
Timber.d("Sending command: CMD_DO_MEASUREMENT");
sendCommand(CMD_DO_MEASUREMENT, encodeUserId(currentRemoteUser));
stopMachineState();
} else {
Timber.d("All finished, nothing to do.");
return false;
}
break;
default:
// Finish init if everything is done
Timber.d("End of state flow reached.");
return false;
}
return true;
}
use of com.health.openscale.core.datatypes.ScaleUser in project openScale by oliexdev.
the class BluetoothBeurerSanitas method processScaleAck.
private void processScaleAck(byte[] data) {
switch(data[2]) {
case CMD_SCALE_STATUS:
Timber.d("ACK type: CMD_SCALE_STATUS");
// data[3] != 0 if an invalid user id is given to the command,
// but it still provides some useful information (e.g. current unit).
final int batteryLevel = data[4] & 0xFF;
final float weightThreshold = (data[5] & 0xFF) / 10f;
final float bodyFatThreshold = (data[6] & 0xFF) / 10f;
final int currentUnit = data[7] & 0xFF;
final boolean userExists = data[8] == 0;
final boolean userReferWeightExists = data[9] == 0;
final boolean userMeasurementExist = data[10] == 0;
final int scaleVersion = data[11] & 0xFF;
Timber.d("Battery level: %d; threshold: weight=%.2f, body fat=%.2f;" + " unit: %d; requested user: exists=%b, has reference weight=%b," + " has measurement=%b; scale version: %d", batteryLevel, weightThreshold, bodyFatThreshold, currentUnit, userExists, userReferWeightExists, userMeasurementExist, scaleVersion);
if (batteryLevel <= 10) {
sendMessage(R.string.info_scale_low_battery, batteryLevel);
}
byte requestedUnit = (byte) currentUnit;
ScaleUser user = OpenScale.getInstance().getSelectedScaleUser();
switch(user.getScaleUnit()) {
case KG:
requestedUnit = 1;
break;
case LB:
requestedUnit = 2;
break;
case ST:
requestedUnit = 4;
break;
}
if (requestedUnit != currentUnit) {
Timber.d("Set scale unit (CMD_SET_UNIT) to %s (%d)", user.getScaleUnit(), requestedUnit);
sendCommand(CMD_SET_UNIT, requestedUnit);
// We send a new command, so make sure we wait
stopMachineState();
} else {
// This should only be received in step 3
if (waitForDataInStep != 3) {
Timber.w("Received ACK for CMD_SCALE_STATUS in wrong state...");
if (waitForDataInStep >= 0) {
Timber.w("...while waiting for other data. Retrying last step.");
// We are in the wrong state.
// This may happen, so let's just retry whatever we did before.
jumpBackOneStep();
} else {
Timber.w("...ignored, no data expected.");
}
}
// All data received, no more waiting.
waitForDataInStep = -1;
resumeMachineState();
}
break;
case CMD_SET_UNIT:
Timber.d("ACK type: CMD_SET_UNIT");
if (data[3] == 0) {
Timber.d("Scale unit successfully set");
}
// This should only be received in step 3
if (waitForDataInStep != 3) {
Timber.w("Received ACK for CMD_SET_UNIT in wrong state...");
if (waitForDataInStep >= 0) {
Timber.w("...while waiting for other data. Retrying last step.");
// We are in the wrong state.
// This may happen, so let's just retry whatever we did before.
jumpBackOneStep();
} else {
Timber.w("...ignored, no data expected.");
}
}
// All data received, no more waiting.
waitForDataInStep = -1;
resumeMachineState();
break;
case CMD_USER_LIST:
Timber.d("ACK type: CMD_USER_LIST");
int userCount = data[4] & 0xFF;
int maxUserCount = data[5] & 0xFF;
Timber.d("Have %d users (max is %d)", userCount, maxUserCount);
if (userCount == 0) {
// This message should only be received in state 4.
if (waitForDataInStep != 4) {
Timber.w("Received ACK for CMD_USER_LIST in wrong state...");
if (waitForDataInStep >= 0) {
Timber.w("...while waiting for other data.");
// We are in the wrong state.
// This may happen, so let's just retry whatever we did before.
jumpBackOneStep();
} else {
Timber.w("...ignored, no data expected.");
}
}
// User list is empty, no more waiting.
waitForDataInStep = -1;
resumeMachineState();
} else {
// More data should be incoming, so make sure we wait
stopMachineState();
}
break;
case CMD_GET_SAVED_MEASUREMENTS:
Timber.d("ACK type: CMD_GET_SAVED_MEASUREMENTS");
int measurementCount = data[3] & 0xFF;
Timber.d("Received ACK for CMD_GET_SAVED_MEASUREMENTS for %d measurements.", measurementCount / 2);
if (measurementCount == 0) {
// We expect no more data, because there are no measurements.
readyForData = true;
// This message should only be received in step 5.
if (waitForDataInStep != 5) {
Timber.w("Received ACK for CMD_GET_SAVED_MEASUREMENTS in wrong state...");
if (waitForDataInStep >= 0) {
Timber.w("...while waiting for other data. Retrying last step.");
// We are in the wrong state.
// This may happen, so let's just retry whatever we did before.
jumpBackOneStep();
} else {
Timber.w("...ignored, no data expected.");
}
}
// No saved data, no more waiting.
waitForDataInStep = -1;
resumeMachineState();
} else // Otherwise wait for CMD_SAVED_MEASUREMENT notifications which will,
// once all measurements have been received, resume the state machine.
{
// More data should be incoming, so make sure we wait
stopMachineState();
}
break;
case CMD_DELETE_SAVED_MEASUREMENTS:
Timber.d("ACK type: CMD_DELETE_SAVED_MEASUREMENTS");
if (data[3] == 0) {
Timber.d("Saved measurements successfully deleted for user " + currentRemoteUser.name);
}
// This message should only be received in state 5, 6 or 8
if (waitForDataInStep != 5 && waitForDataInStep != 6 && waitForDataInStep != 8) {
Timber.w("Received ACK for CMD_DELETE_SAVED_MEASUREMENTS in wrong state...");
if (waitForDataInStep >= 0) {
Timber.w("...while waiting for other data. Retrying last step.");
// We are in the wrong state.
// This may happen, so let's just retry whatever we did before.
jumpBackOneStep();
} else {
Timber.w("...ignored, no data expected.");
}
}
// All data received, no more waiting.
waitForDataInStep = -1;
resumeMachineState();
break;
case CMD_USER_ADD:
Timber.d("ACK type: CMD_USER_ADD");
// This message should only be received in state 6
if (waitForDataInStep != 6) {
Timber.w("Received ACK for CMD_USER_ADD in wrong state...");
if (waitForDataInStep >= 0) {
Timber.w("...while waiting for other data. Retrying last step.");
// We are in the wrong state.
// This may happen, so let's just retry whatever we did before.
jumpBackOneStep();
} else {
Timber.w("...ignored, no data expected.");
}
// No more data expected after this command.
waitForDataInStep = -1;
resumeMachineState();
// Get out of here, this wasn't supposed to happen.
break;
}
if (data[3] == 0) {
remoteUsers.add(currentRemoteUser);
// If we have unprocessed data available, store it now.
if (storedMeasurement.measurementData != null) {
Timber.d("User identified, storing unprocessed data.");
addMeasurement(storedMeasurement.measurementData, currentRemoteUser.localUserId);
storedMeasurement.measurementData = null;
}
// We can now receive and process data, user has been identified and send to the scale.
readyForData = true;
// Try to start a measurement to make the scale learn the reference weight to recognize the user next time.
// If we already have data, this will most likely run into time-out and the scale switches off before finishing.
Timber.d("New user successfully added; time to step on scale");
sendMessage(R.string.info_step_on_scale_for_reference, 0);
Timber.d("Sending command: CMD_DO_MEASUREMENT");
sendCommand(CMD_DO_MEASUREMENT, encodeUserId(currentRemoteUser));
// We send a new command, so make sure we wait
stopMachineState();
break;
}
Timber.d("Cannot create additional scale user (error 0x%02x)", data[3]);
sendMessage(R.string.error_max_scale_users, 0);
// Force disconnect
Timber.d("Terminating state machine.");
jumpNextToStepNr(9);
// All data received, no more waiting.
waitForDataInStep = -1;
resumeMachineState();
break;
case CMD_DO_MEASUREMENT:
Timber.d("ACK type: CMD_DO_MEASUREMENT");
if (data[3] != 0) {
Timber.d("Measure command rejected.");
// This message should only be received in state 6 or 8
if (waitForDataInStep != 6 && waitForDataInStep != 8) {
Timber.w("Received ACK for CMD_DO_MEASUREMENT in wrong state...");
if (waitForDataInStep >= 0) {
Timber.w("...while waiting for other data. Retrying last step.");
// We are in the wrong state.
// This may happen, so let's just retry whatever we did before.
jumpBackOneStep();
} else {
Timber.w("...ignored, no data expected.");
}
// No more data expected after this command.
waitForDataInStep = -1;
resumeMachineState();
// Get out of here, this wasn't supposed to happen.
break;
}
} else {
Timber.d("Measure command successfully received");
sendMessage(R.string.info_step_on_scale, 0);
// More data should be incoming, so make sure we wait
stopMachineState();
}
break;
case CMD_USER_DETAILS:
Timber.d("ACK type: CMD_USER_DETAILS");
if (data[3] == 0) {
String name = decodeString(data, 4, 3);
int year = 1900 + (data[7] & 0xFF);
int month = 1 + (data[8] & 0xFF);
int day = data[9] & 0xFF;
int height = data[10] & 0xFF;
boolean male = (data[11] & 0xF0) != 0;
int activity = data[11] & 0x0F;
Timber.d("Name: %s, Birthday: %d-%02d-%02d, Height: %d, Sex: %s, activity: %d", name, year, month, day, height, male ? "male" : "female", activity);
}
// This message should only be received in state 7
if (waitForDataInStep != 7) {
Timber.w("Received ACK for CMD_USER_DETAILS in wrong state...");
if (waitForDataInStep >= 0) {
Timber.w("...while waiting for other data. Retrying last step.");
// We are in the wrong state.
// This may happen, so let's just retry whatever we did before.
jumpBackOneStep();
} else {
Timber.w("...ignored, no data expected.");
}
}
// All data received, no more waiting.
waitForDataInStep = -1;
resumeMachineState();
break;
default:
Timber.d("Unhandled scale ack for command 0x%02x", data[2]);
break;
}
}
use of com.health.openscale.core.datatypes.ScaleUser in project openScale by oliexdev.
the class BluetoothDigooDGSO38H method parseBytes.
private void parseBytes(byte[] weightBytes) {
float weight, fat, water, muscle, boneWeight, visceralFat;
// float subcutaneousFat, metabolicBaseRate, biologicalAge, boneWeight;
final byte ctrlByte = weightBytes[5];
final boolean allValues = isBitSet(ctrlByte, 1);
final boolean weightStabilized = isBitSet(ctrlByte, 0);
final ScaleUser selectedUser = OpenScale.getInstance().getSelectedScaleUser();
if (weightStabilized) {
// The weight is stabilized, now we want to measure all available values
byte gender = selectedUser.getGender().isMale() ? (byte) 0x00 : (byte) 0x01;
byte height = (byte) (((int) selectedUser.getBodyHeight()) & 0xFF);
byte age = (byte) (selectedUser.getAge() & 0xff);
// kg
byte unit = 0x01;
switch(selectedUser.getScaleUnit()) {
case LB:
unit = 0x02;
break;
case ST:
unit = 0x8;
break;
}
byte[] configBytes = new byte[] { (byte) 0x09, (byte) 0x10, (byte) 0x12, (byte) 0x11, (byte) 0x0d, (byte) 0x01, height, age, gender, unit, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
// Write checksum is sum of all bytes % 256
int checksum = 0x00;
for (int i = 3; i < configBytes.length - 1; i++) {
checksum += configBytes[i];
}
configBytes[15] = (byte) (checksum & 0xFF);
writeBytes(WEIGHT_MEASUREMENT_SERVICE, EXTRA_MEASUREMENT_CHARACTERISTIC, configBytes);
} else if (allValues) {
ScaleMeasurement scaleBtData = new ScaleMeasurement();
weight = (float) (((weightBytes[3] & 0xFF) << 8) | (weightBytes[4] & 0xFF)) / 100.0f;
fat = (float) (((weightBytes[6] & 0xFF) << 8) | (weightBytes[7] & 0xFF)) / 10.0f;
if (Math.abs(fat - 0.0) < 0.00001) {
Timber.d("Scale signaled that measurement of all data " + "is done, but fat is still zero. Settling for just adding weight.");
} else {
// subcutaneousFat = (float) (((weightBytes[8] & 0xFF) << 8) | (weightBytes[9] & 0xFF)) / 10.0f;
visceralFat = (float) (weightBytes[10] & 0xFF) / 10.0f;
water = (float) (((weightBytes[11] & 0xFF) << 8) | (weightBytes[12] & 0xFF)) / 10.0f;
// metabolicBaseRate = (float) (((weightBytes[13] & 0xFF) << 8) | (weightBytes[14] & 0xFF));
// biologicalAge = (float) (weightBytes[15] & 0xFF) + 1;
muscle = (float) (((weightBytes[16] & 0xFF) << 8) | (weightBytes[17] & 0xFF)) / 10.0f;
boneWeight = (float) (weightBytes[18] & 0xFF) / 10.0f;
scaleBtData.setDateTime(new Date());
scaleBtData.setFat(fat);
scaleBtData.setMuscle(muscle);
scaleBtData.setWater(water);
scaleBtData.setBone(boneWeight);
scaleBtData.setVisceralFat(visceralFat);
}
scaleBtData.setWeight(weight);
addScaleMeasurement(scaleBtData);
}
}
use of com.health.openscale.core.datatypes.ScaleUser in project openScale by oliexdev.
the class OpenScale method addScaleMeasurement.
public int addScaleMeasurement(final ScaleMeasurement scaleMeasurement, boolean silent) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
// Check user id and do a smart user assign if option is enabled
if (scaleMeasurement.getUserId() == -1) {
if (prefs.getBoolean("smartUserAssign", false)) {
scaleMeasurement.setUserId(getSmartUserAssignment(scaleMeasurement.getWeight(), 15.0f));
} else {
scaleMeasurement.setUserId(getSelectedScaleUser().getId());
}
// don't add scale data if no user is selected
if (scaleMeasurement.getUserId() == -1) {
Timber.e("to be added measurement are thrown away because no user is selected");
return -1;
}
}
// Assisted weighing
if (getScaleUser(scaleMeasurement.getUserId()).isAssistedWeighing()) {
int assistedWeighingRefUserId = prefs.getInt("assistedWeighingRefUserId", -1);
if (assistedWeighingRefUserId != -1) {
ScaleMeasurement lastRefScaleMeasurement = getLastScaleMeasurement(assistedWeighingRefUserId);
if (lastRefScaleMeasurement != null) {
float refWeight = lastRefScaleMeasurement.getWeight();
float diffToRef = scaleMeasurement.getWeight() - refWeight;
scaleMeasurement.setWeight(diffToRef);
}
} else {
Timber.e("assisted weighing reference user id is -1");
}
}
// Calculate the amputation correction factor for the weight, if available
scaleMeasurement.setWeight((scaleMeasurement.getWeight() * 100.0f) / getScaleUser(scaleMeasurement.getUserId()).getAmputationCorrectionFactor());
// If option is enabled then calculate body measurements from generic formulas
MeasurementViewSettings settings = new MeasurementViewSettings(prefs, WaterMeasurementView.KEY);
if (settings.isEnabled() && settings.isEstimationEnabled()) {
EstimatedWaterMetric waterMetric = EstimatedWaterMetric.getEstimatedMetric(EstimatedWaterMetric.FORMULA.valueOf(settings.getEstimationFormula()));
scaleMeasurement.setWater(waterMetric.getWater(getScaleUser(scaleMeasurement.getUserId()), scaleMeasurement));
}
settings = new MeasurementViewSettings(prefs, FatMeasurementView.KEY);
if (settings.isEnabled() && settings.isEstimationEnabled()) {
EstimatedFatMetric fatMetric = EstimatedFatMetric.getEstimatedMetric(EstimatedFatMetric.FORMULA.valueOf(settings.getEstimationFormula()));
scaleMeasurement.setFat(fatMetric.getFat(getScaleUser(scaleMeasurement.getUserId()), scaleMeasurement));
}
// Must be after fat estimation as one formula is based on fat
settings = new MeasurementViewSettings(prefs, LBMMeasurementView.KEY);
if (settings.isEnabled() && settings.isEstimationEnabled()) {
EstimatedLBMMetric lbmMetric = EstimatedLBMMetric.getEstimatedMetric(EstimatedLBMMetric.FORMULA.valueOf(settings.getEstimationFormula()));
scaleMeasurement.setLbm(lbmMetric.getLBM(getScaleUser(scaleMeasurement.getUserId()), scaleMeasurement));
}
// Insert measurement into the database, check return if it was successful inserted
if (measurementDAO.insert(scaleMeasurement) != -1) {
Timber.d("Added measurement: %s", scaleMeasurement);
if (!silent) {
ScaleUser scaleUser = getScaleUser(scaleMeasurement.getUserId());
final java.text.DateFormat dateFormat = DateFormat.getDateFormat(context);
final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(context);
final Date dateTime = scaleMeasurement.getDateTime();
final Converters.WeightUnit unit = scaleUser.getScaleUnit();
String infoText = String.format(context.getString(R.string.info_new_data_added), Converters.fromKilogram(scaleMeasurement.getWeight(), unit), unit.toString(), dateFormat.format(dateTime) + " " + timeFormat.format(dateTime), scaleUser.getUserName());
runUiToastMsg(infoText);
}
syncInsertMeasurement(scaleMeasurement);
alarmHandler.entryChanged(context, scaleMeasurement);
triggerWidgetUpdate();
} else {
Timber.d("to be added measurement is thrown away because measurement with the same date and time already exist");
if (!silent) {
runUiToastMsg(context.getString(R.string.info_new_data_duplicated));
}
}
return scaleMeasurement.getUserId();
}
Aggregations