1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-27 12:26:48 +01:00

Add device support for the IKEA desk controller

All the required functionality is included:
- Move the desk up and down manually
- See the current height and speed
- Store three desk positions
- Move to any of those desk positions
This commit is contained in:
Linos Giannopoulos 2024-10-12 21:35:18 +03:00 committed by José Rebelo
parent 25dc3f972b
commit f3b4f0ed26
13 changed files with 725 additions and 1 deletions

View File

@ -211,6 +211,10 @@
android:name=".devices.vesc.VescControlActivity"
android:label="@string/devicetype_vesc"
android:parentActivityName=".activities.ControlCenterv2" />
<activity
android:name=".devices.idasen.IdasenControlActivity"
android:label="@string/devicetype_idasen"
android:parentActivityName=".activities.ControlCenterv2" />
<activity
android:name=".activities.FwAppInstallerActivity"
android:label="@string/title_activity_fw_app_insaller"
@ -908,4 +912,4 @@
android:exported="true" />
</application>
</manifest>
</manifest>

View File

@ -482,6 +482,10 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_VESC_MINIMUM_VOLTAGE = "vesc_minimum_battery_voltage";
public static final String PREF_VESC_MAXIMUM_VOLTAGE = "vesc_maximum_battery_voltage";
public static final String PREF_IDASEN_SIT_HEIGHT = "idasen_sit_height";
public static final String PREF_IDASEN_MID_HEIGHT = "idasen_mid_height";
public static final String PREF_IDASEN_STAND_HEIGHT = "idasen_stand_height";
public static final String PREF_SOUNDS = "sounds";
public static final String PREF_AUTH_KEY = "authkey";
public static final String PREF_USER_FITNESS_GOAL = "fitness_goal";

View File

@ -0,0 +1,30 @@
package nodomain.freeyourgadget.gadgetbridge.devices.idasen;
import java.util.UUID;
public class IdasenConstants {
public static final UUID CHARACTERISTIC_SVC_HEIGHT = UUID.fromString("99fa0020-338a-1024-8a49-009c0215f78a");
public static final UUID CHARACTERISTIC_SVC_COMMAND = UUID.fromString("99fa0001-338a-1024-8a49-009c0215f78a");
public static final UUID CHARACTERISTIC_COMMAND = UUID.fromString("99fa0002-338a-1024-8a49-009c0215f78a");
public static final UUID CHARACTERISTIC_SVC_REF_HEIGHT = UUID.fromString("99fa0030-338a-1024-8a49-009c0215f78a");
public static final UUID CHARACTERISTIC_SVC_DPG = UUID.fromString("99fa0010-338a-1024-8a49-009c0215f78a");
public static final UUID CHARACTERISTIC_REF_HEIGHT = UUID.fromString("99fa0031-338a-1024-8a49-009c0215f78a");
public static final UUID CHARACTERISTIC_HEIGHT = UUID.fromString("99fa0021-338a-1024-8a49-009c0215f78a");
public static final UUID CHARACTERISTIC_DPG = UUID.fromString("99fa0011-338a-1024-8a49-009c0215f78a");
public static final String ACTION_REALTIME_DESK_VALUES = ".action.realtime_desk_values";
public static final String EXTRA_DESK_HEIGHT = "EXTRA_DESK_HEIGHT";
public static final String EXTRA_DESK_SPEED = "EXTRA_DESK_SPEED";
public static final double MIN_HEIGHT = 0.62;
public static final double MAX_HEIGHT = 1.27;
public static final byte[] CMD_UP = new byte[]{0x47, 0x00};
public static final byte[] CMD_DOWN = new byte[]{0x46, 0x00};
public static final byte[] CMD_STOP = new byte[]{(byte)0xFF, 0x00};
public static final byte[] CMD_WAKEUP = new byte[]{(byte)0xFE, 0x00};
public static final byte[] CMD_REF_INPUT_STOP = new byte[]{0x01, (byte)0x80};
public static final byte[] CMD_DPG_WAKEUP_PREP = new byte[]{0x7F, (byte)0x86, 0x00};
public static final byte[] CMD_DPG_WAKEUP = new byte[]{0x7F, (byte)0x86, (byte)0x80, 0x01,
0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11};
}

View File

@ -0,0 +1,105 @@
package nodomain.freeyourgadget.gadgetbridge.devices.idasen;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.WorkoutValueFormatter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.idasen.IdasenDeviceSupport;
public class IdasenControlActivity extends AbstractGBActivity {
private final Logger logger = LoggerFactory.getLogger(getClass());
public TextView mDeskHeight, mDeskSpeed;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Objects.requireNonNull(action).equals(IdasenConstants.ACTION_REALTIME_DESK_VALUES)){
final WorkoutValueFormatter formatter = new WorkoutValueFormatter();
float height = (float) intent.getSerializableExtra(IdasenConstants.EXTRA_DESK_HEIGHT);
float speed = (float) intent.getSerializableExtra(IdasenConstants.EXTRA_DESK_SPEED);
logger.debug("Received desk values: {} {}", speed, height);
mDeskHeight.setText(formatter.formatValue(height * 100F, "cm"));
mDeskSpeed.setText(formatter.formatValue(speed * 1000F, "mm/s"));
}
}
};
LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_idasen_control);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
mDeskHeight = findViewById(R.id.idasen_desk_height);
mDeskSpeed = findViewById(R.id.idasen_desk_speed);
IntentFilter filterLocal = new IntentFilter();
filterLocal.addAction(IdasenConstants.ACTION_REALTIME_DESK_VALUES);
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(mReceiver, filterLocal);
Intent intent = new Intent(IdasenDeviceSupport.COMMAND_GET_DESK_VALUES);
sendLocalBroadcast(intent);
View.OnTouchListener controlTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Intent intent = null;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (v.getId() == R.id.idasen_control_button_up) {
intent = new Intent(IdasenDeviceSupport.COMMAND_UP);
} else if (v.getId() == R.id.idasen_control_button_down) {
intent = new Intent(IdasenDeviceSupport.COMMAND_DOWN);
} else if (v.getId() == R.id.idasen_control_button_sit) {
intent = new Intent(IdasenDeviceSupport.COMMAND_SET_HEIGHT);
intent.putExtra(IdasenDeviceSupport.TARGET_HEIGHT, IdasenDeviceSupport.TARGET_POS_SIT);
} else if (v.getId() == R.id.idasen_control_button_stand) {
intent = new Intent(IdasenDeviceSupport.COMMAND_SET_HEIGHT);
intent.putExtra(IdasenDeviceSupport.TARGET_HEIGHT, IdasenDeviceSupport.TARGET_POS_STAND);
} else if (v.getId() == R.id.idasen_control_button_mid) {
intent = new Intent(IdasenDeviceSupport.COMMAND_SET_HEIGHT);
intent.putExtra(IdasenDeviceSupport.TARGET_HEIGHT, IdasenDeviceSupport.TARGET_POS_MID);
}
sendLocalBroadcast(intent);
} else {
return false;
}
return true;
}
};
findViewById(R.id.idasen_control_button_up).setOnTouchListener(controlTouchListener);
findViewById(R.id.idasen_control_button_down).setOnTouchListener(controlTouchListener);
findViewById(R.id.idasen_control_button_sit).setOnTouchListener(controlTouchListener);
findViewById(R.id.idasen_control_button_mid).setOnTouchListener(controlTouchListener);
findViewById(R.id.idasen_control_button_stand).setOnTouchListener(controlTouchListener);
}
private void sendLocalBroadcast(Intent intent) {
localBroadcastManager.sendBroadcast(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(mReceiver);
}
}

View File

@ -0,0 +1,70 @@
package nodomain.freeyourgadget.gadgetbridge.devices.idasen;
import android.app.Activity;
import androidx.annotation.NonNull;
import java.util.regex.Pattern;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
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.idasen.IdasenDeviceSupport;
public class IdasenCoordinator extends AbstractBLEDeviceCoordinator {
@Override
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
}
@Override
public String getManufacturer() {
return "IKEA";
}
@Override
protected Pattern getSupportedDeviceName() {
return Pattern.compile("Desk 1000", Pattern.CASE_INSENSITIVE);
}
@Override
public int getDeviceNameResource() {
return R.string.devicetype_idasen;
}
@NonNull
@Override
public Class<? extends DeviceSupport> getDeviceSupportClass() {
return IdasenDeviceSupport.class;
}
@Override
public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) {
return new IdasenSettingsCustomizer();
}
@Override
public int getBondingStyle() {
return BONDING_STYLE_BOND;
}
@Override
public boolean supportsAppsManagement(final GBDevice device) {
return true;
}
@Override
public Class<? extends Activity> getAppsManagementActivity() {
return IdasenControlActivity.class;
}
@Override
public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
return new int[]{
R.xml.devicesettings_idasen
};
}
}

View File

@ -0,0 +1,83 @@
package nodomain.freeyourgadget.gadgetbridge.devices.idasen;
import android.os.Parcel;
import android.text.InputType;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.preference.EditTextPreference;
import androidx.preference.Preference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.R;
import static nodomain.freeyourgadget.gadgetbridge.util.GB.toast;
public class IdasenSettingsCustomizer implements DeviceSpecificSettingsCustomizer {
public static final Creator<IdasenSettingsCustomizer> CREATOR = new Creator<IdasenSettingsCustomizer>() {
@Override
public IdasenSettingsCustomizer createFromParcel(final Parcel in) {
return new IdasenSettingsCustomizer();
}
@Override
public IdasenSettingsCustomizer[] newArray(final int size) {
return new IdasenSettingsCustomizer[size];
}
};
private static final Logger LOG = LoggerFactory.getLogger(IdasenSettingsCustomizer.class);
@Override
public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) {
}
@Override
public void customizeSettings(final DeviceSpecificSettingsHandler handler, final Prefs prefs, final String rootKey) {
final EditTextPreference prefMidHeight = handler.findPreference(DeviceSettingsPreferenceConst.PREF_IDASEN_MID_HEIGHT);
final EditTextPreference prefSitHeight = handler.findPreference(DeviceSettingsPreferenceConst.PREF_IDASEN_SIT_HEIGHT);
final EditTextPreference prefStandHeight = handler.findPreference(DeviceSettingsPreferenceConst.PREF_IDASEN_STAND_HEIGHT);
if (prefSitHeight == null || prefMidHeight == null || prefStandHeight == null) {
return;
}
Preference.OnPreferenceChangeListener prefListener = (preference, newValue) -> {
final double val = Double.parseDouble(newValue.toString());
if (val > IdasenConstants.MAX_HEIGHT * 100F || val < IdasenConstants.MIN_HEIGHT * 100F) {
toast(handler.getContext(), R.string.idasen_pref_value_warning, Toast.LENGTH_SHORT, 0);
return false;
}
return true;
};
for (EditTextPreference pref: List.of(prefMidHeight, prefSitHeight, prefStandHeight)){
pref.setOnBindEditTextListener(p -> {
p.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL);
p.setSelection(p.getText().length());
});
pref.setOnPreferenceChangeListener(prefListener);
}
}
@Override
public Set<String> getPreferenceKeysWithSummary() {
return Collections.emptySet();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
}
}

View File

@ -195,6 +195,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huawei.huaweiwatchgtrunner.H
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.huaweiwatchultimate.HuaweiWatchUltimateCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.id115.ID115Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.itag.ITagCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.idasen.IdasenCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.BFH16DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.TeclastH30.TeclastH30Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.jyou.y5.Y5Coordinator;
@ -406,6 +407,7 @@ public enum DeviceType {
COLACAO21(ColaCao21Coordinator.class),
COLACAO23(ColaCao23Coordinator.class),
ITAG(ITagCoordinator.class),
IKEA_IDASEN(IdasenCoordinator.class),
NUTMINI(NutCoordinator.class),
VIVOMOVE_HR(GarminVivomoveHrCoordinator.class),
GARMIN_ENDURO_3(GarminEnduro3Coordinator.class),

View File

@ -0,0 +1,208 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.idasen;
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 android.content.SharedPreferences;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Objects;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.devices.idasen.IdasenConstants;
public class IdasenDeviceSupport extends AbstractBTLEDeviceSupport {
private static final Logger LOG = LoggerFactory.getLogger(IdasenDeviceSupport.class);
public static final String COMMAND_UP = "nodomain.freeyourgadget.gadgetbridge.idasen.command.UP";
public static final String COMMAND_DOWN = "nodomain.freeyourgadget.gadgetbridge.idasen.command.DOWN";
public static final String COMMAND_SET_HEIGHT = "nodomain.freeyourgadget.gadgetbridge.idasen.command.SET_HEIGHT";
public static final String COMMAND_GET_DESK_VALUES = "nodomain.freeyourgadget.gadgetbridge.idasen.command.GET_DESK_VALUES";
public static final String TARGET_HEIGHT = "TARGET_HEIGHT";
public static final String TARGET_POS_SIT = "TARGET_POS_SIT";
public static final String TARGET_POS_STAND = "TARGET_POS_STAND";
public static final String TARGET_POS_MID = "TARGET_POS_MID";
public float sit_height, stand_height, mid_height;
public float deskSpeed, deskHeight;
public IdasenDeviceSupport() {
super(LOG);
addSupportedService(IdasenConstants.CHARACTERISTIC_SVC_HEIGHT);
addSupportedService(IdasenConstants.CHARACTERISTIC_SVC_COMMAND);
addSupportedService(IdasenConstants.CHARACTERISTIC_SVC_REF_HEIGHT);
addSupportedService(IdasenConstants.CHARACTERISTIC_SVC_DPG);
}
BroadcastReceiver commandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String act = intent.getAction();
switch(Objects.requireNonNull(act)){
case COMMAND_UP:
sendCommand("cmd", IdasenConstants.CHARACTERISTIC_COMMAND, IdasenConstants.CMD_UP);
break;
case COMMAND_DOWN:
sendCommand("cmd", IdasenConstants.CHARACTERISTIC_COMMAND, IdasenConstants.CMD_DOWN);
break;
case COMMAND_GET_DESK_VALUES:
readCharacteristic("IdasenGetHeight", IdasenConstants.CHARACTERISTIC_HEIGHT);
break;
case COMMAND_SET_HEIGHT:
HashMap<String, Float> mPositionsMap = new HashMap<>();
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
stand_height = Float.parseFloat(prefs.getString(DeviceSettingsPreferenceConst.PREF_IDASEN_STAND_HEIGHT, "0.0")) / 100F;
mid_height = Float.parseFloat(prefs.getString(DeviceSettingsPreferenceConst.PREF_IDASEN_MID_HEIGHT, "0.0")) / 100F;
sit_height = Float.parseFloat(prefs.getString(DeviceSettingsPreferenceConst.PREF_IDASEN_SIT_HEIGHT, "0.0")) / 100F;
mPositionsMap.put(TARGET_POS_SIT, sit_height);
mPositionsMap.put(TARGET_POS_STAND, stand_height);
mPositionsMap.put(TARGET_POS_MID, mid_height);
sendCommand("cmd", IdasenConstants.CHARACTERISTIC_COMMAND, IdasenConstants.CMD_WAKEUP);
sendCommand("cmd", IdasenConstants.CHARACTERISTIC_COMMAND, IdasenConstants.CMD_STOP);
BluetoothGattCharacteristic characteristic = getCharacteristic(IdasenConstants.CHARACTERISTIC_REF_HEIGHT);
String targetHeightKey = intent.getStringExtra(TARGET_HEIGHT);
float targetHeight = mPositionsMap.get(targetHeightKey);
ByteBuffer setHeightBB = ByteBuffer.allocate(2);
setHeightBB.order(ByteOrder.LITTLE_ENDIAN);
setHeightBB.putShort(0, (short) ((targetHeight - IdasenConstants.MIN_HEIGHT) * 10000F));
byte[] setHeightRequest = setHeightBB.array();
new Thread(() -> {
// This acts as a fail-safe, in case deskSpeed never goes to 0
// for whatever reason. It's based on the time the desk controller
// needs to get from the lowest to the highest point.
int cutOff = 100;
do {
TransactionBuilder builder = new TransactionBuilder("height");
builder.write(characteristic, setHeightRequest);
builder.queue(getQueue());
try {
Thread.sleep(300);
} catch (InterruptedException e) {
GB.log("error", GB.ERROR, e);
}
cutOff--;
} while (deskSpeed != 0F && cutOff != 0);
if (cutOff == 0) {
LOG.warn("desk controller did not reach the desired height in time");
}
}).start();
break;
}
}
};
@Override
public boolean useAutoConnect() {
return true;
}
@Override
public void dispose() {
super.dispose();
LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(commandReceiver);
}
@Override
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
builder.notify(getCharacteristic(IdasenConstants.CHARACTERISTIC_HEIGHT), true);
sendCommand("dpg", IdasenConstants.CHARACTERISTIC_DPG, IdasenConstants.CMD_DPG_WAKEUP_PREP);
sendCommand("dpg", IdasenConstants.CHARACTERISTIC_DPG, IdasenConstants.CMD_DPG_WAKEUP);
sendCommand("dpg", IdasenConstants.CHARACTERISTIC_COMMAND, IdasenConstants.CMD_WAKEUP);
initBroadcast();
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
return builder;
}
@Override
public boolean onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (characteristic.getUuid().equals(IdasenConstants.CHARACTERISTIC_HEIGHT)) {
getDeskValues(characteristic);
announceDeskValues();
return true;
}
return super.onCharacteristicRead(gatt, characteristic, status);
}
@Override
public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (characteristic.getUuid().equals(IdasenConstants.CHARACTERISTIC_HEIGHT)) {
getDeskValues(characteristic);
announceDeskValues();
return true;
}
return super.onCharacteristicChanged(gatt, characteristic);
}
private void readCharacteristic(String taskName, UUID charac) {
BluetoothGattCharacteristic characteristic = getCharacteristic(charac);
TransactionBuilder builder = new TransactionBuilder(taskName);
builder.read(characteristic);
builder.queue(getQueue());
}
private void initBroadcast() {
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getContext());
IntentFilter filter = new IntentFilter();
filter.addAction(COMMAND_UP);
filter.addAction(COMMAND_DOWN);
filter.addAction(COMMAND_SET_HEIGHT);
filter.addAction(COMMAND_GET_DESK_VALUES);
broadcastManager.registerReceiver(commandReceiver, filter);
}
private void getDeskValues(BluetoothGattCharacteristic characteristic) {
byte[] value = characteristic.getValue();
final ByteBuffer buf = ByteBuffer.wrap(value).order(ByteOrder.LITTLE_ENDIAN);
int hh = BLETypeConversions.toUnsigned(buf.getShort());
deskHeight = (float) IdasenConstants.MIN_HEIGHT + hh / 10000F;
deskSpeed = buf.getShort() / 10000F;
}
private void announceDeskValues() {
final Intent intent = new Intent(IdasenConstants.ACTION_REALTIME_DESK_VALUES)
.putExtra(IdasenConstants.EXTRA_DESK_HEIGHT, deskHeight)
.putExtra(IdasenConstants.EXTRA_DESK_SPEED, deskSpeed);
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
}
private void sendCommand(String taskName, UUID charac, byte[] contents) {
TransactionBuilder builder = new TransactionBuilder(taskName);
BluetoothGattCharacteristic characteristic = getCharacteristic(charac);
if (characteristic != null) {
builder.write(characteristic, contents);
builder.queue(getQueue());
}
}
}

View File

@ -0,0 +1,41 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,20h-6a2,2 0,0 1,-2 -2v-12a2,2 0,0 1,2 -2h6"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M18,14v7"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M18,3v7"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M15,18l3,3l3,-3"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M15,6l3,-3l3,3"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFFFF"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5.636,19.364a9,9 0,1 1,12.728 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFFFF"
android:strokeLineCap="round"/>
<path
android:pathData="M16,9l-4,4"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#FFFFFFFF"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/idasen_control_button_sit"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_weight="0.5"
android:text="@string/idasen_control_button_sit"
app:icon="@drawable/ic_activity_yoga"
app:iconPadding="0dp"
app:iconSize="35dp"
app:iconTint="#FFFFFF" />
<Button
android:id="@+id/idasen_control_button_mid"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_weight="0.5"
android:text="@string/idasen_control_button_mid"
app:icon="@drawable/ic_activity_bike_trainer"
app:iconSize="35dp" />
<Button
android:id="@+id/idasen_control_button_stand"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_weight="0.5"
android:text="@string/idasen_control_button_stand"
app:icon="@drawable/ic_activity_exercise"
app:iconSize="35dp"
app:iconTint="#FFFFFF" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/idasen_control_button_up"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_weight="0.5"
android:background="@drawable/ic_arrow_upward" />
<Button
android:id="@+id/idasen_control_button_down"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_weight="0.5"
android:background="@drawable/ic_arrow_upward"
android:drawable="@drawable/ic_arrow_upward"
android:rotation="180" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/idasen_desk_height_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="start|center_vertical"
android:background="@drawable/arrow_autofit_height"
android:backgroundTint="#FF0000"
android:contentDescription="@string/icon_placeholder"
android:padding="0dp" />
<TextView
android:id="@+id/idasen_desk_height"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:textIsSelectable="true"
android:textAlignment="center"
android:textColor="#FFFFFF"
android:textSize="24sp" />
<ImageView
android:id="@+id/idasen_desk_speed_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="start|center_vertical"
android:background="@drawable/brand_speedtest"
android:backgroundTint="#FF0000"
android:contentDescription="@string/icon_placeholder"
android:padding="0dp" />
<TextView
android:id="@+id/idasen_desk_speed"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:textAlignment="center"
android:textColor="#FFFFFF"
android:textSize="24sp"
android:textIsSelectable="true"
android:visibility="visible" />
</LinearLayout>
</LinearLayout>

View File

@ -1792,6 +1792,7 @@
<string name="devicetype_mismartscale">Mi Smart Scale 2</string>
<string name="devicetype_micompositionscale">Mi Body Composition Scale 2</string>
<string name="devicetype_itag">iTag</string>
<string name="devicetype_idasen">IKEA Idasen Desk</string>
<string name="devicetype_bfh16">BFH-16</string>
<string name="devicetype_mijia_lywsd02">Mijia Smart Clock</string>
<string name="devicetype_mijia_lywsd03">Mijia Temperature and Humidity Sensor 2</string>
@ -2798,6 +2799,13 @@
<!-- Translators: the ### indicate number of digits, keep intact -->
<string name="distance_format_feet">###ft</string>
<string name="idasen_control_button_mid">MID</string>
<string name="idasen_control_button_sit">SIT</string>
<string name="idasen_control_button_stand">STAND</string>
<string name="idasen_pref_mid_height">Middle position height (in centimeters)</string>
<string name="idasen_pref_sit_height">Sit position height (in centimeters)</string>
<string name="idasen_pref_stand_height">Stand position height (in centimeters)</string>
<string name="idasen_pref_value_warning">The value must be between 62 - 126 cm</string>
<string name="prefs_workmode">Work Mode</string>
<string name="huawei_alarm_smart_description">Do not uncheck smart wakeup checkbox.</string>
<string name="huawei_alarm_event_description">Do not check smart wakeup checkbox.</string>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<EditTextPreference
android:title="@string/idasen_pref_sit_height"
android:key="idasen_sit_height"
android:inputType="number"
android:icon="@drawable/ic_activity_yoga"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
android:title="@string/idasen_pref_mid_height"
android:key="idasen_mid_height"
android:inputType="number"
android:icon="@drawable/ic_activity_bike_trainer"
app:useSimpleSummaryProvider="true" />
<EditTextPreference
android:title="@string/idasen_pref_stand_height"
android:key="idasen_stand_height"
android:inputType="number"
android:icon="@drawable/ic_activity_exercise"
app:useSimpleSummaryProvider="true" />
</PreferenceScreen>