diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9de4385ec..ffbee889b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -211,6 +211,10 @@
android:name=".devices.vesc.VescControlActivity"
android:label="@string/devicetype_vesc"
android:parentActivityName=".activities.ControlCenterv2" />
+
-
\ No newline at end of file
+
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
index 141768f8b..2e7c2619c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
@@ -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";
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenConstants.java
new file mode 100644
index 000000000..08402ac3f
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenConstants.java
@@ -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};
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenControlActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenControlActivity.java
new file mode 100644
index 000000000..cb08e6470
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenControlActivity.java
@@ -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);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenCoordinator.java
new file mode 100644
index 000000000..7523dc0d3
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenCoordinator.java
@@ -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
+ };
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenSettingsCustomizer.java
new file mode 100644
index 000000000..c36faa037
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/idasen/IdasenSettingsCustomizer.java
@@ -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 CREATOR = new Creator() {
+ @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 getPreferenceKeysWithSummary() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ }
+}
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 b6be12da2..f92b9dc1b 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
@@ -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),
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/idasen/IdasenDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/idasen/IdasenDeviceSupport.java
new file mode 100644
index 000000000..47ead980b
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/idasen/IdasenDeviceSupport.java
@@ -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 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());
+ }
+ }
+}
diff --git a/app/src/main/res/drawable/arrow_autofit_height.xml b/app/src/main/res/drawable/arrow_autofit_height.xml
new file mode 100644
index 000000000..aa096431c
--- /dev/null
+++ b/app/src/main/res/drawable/arrow_autofit_height.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/brand_speedtest.xml b/app/src/main/res/drawable/brand_speedtest.xml
new file mode 100644
index 000000000..e18f1e762
--- /dev/null
+++ b/app/src/main/res/drawable/brand_speedtest.xml
@@ -0,0 +1,20 @@
+
+
+
+
diff --git a/app/src/main/res/layout/activity_idasen_control.xml b/app/src/main/res/layout/activity_idasen_control.xml
new file mode 100644
index 000000000..7fa59991f
--- /dev/null
+++ b/app/src/main/res/layout/activity_idasen_control.xml
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7f59539e8..efb059f14 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1792,6 +1792,7 @@
Mi Smart Scale 2
Mi Body Composition Scale 2
iTag
+ IKEA Idasen Desk
BFH-16
Mijia Smart Clock
Mijia Temperature and Humidity Sensor 2
@@ -2798,6 +2799,13 @@
###ft
+ MID
+ SIT
+ STAND
+ Middle position height (in centimeters)
+ Sit position height (in centimeters)
+ Stand position height (in centimeters)
+ The value must be between 62 - 126 cm
Work Mode
Do not uncheck smart wakeup checkbox.
Do not check smart wakeup checkbox.
diff --git a/app/src/main/res/xml/devicesettings_idasen.xml b/app/src/main/res/xml/devicesettings_idasen.xml
new file mode 100644
index 000000000..4998911a2
--- /dev/null
+++ b/app/src/main/res/xml/devicesettings_idasen.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+