rhyst / linak-controller

A Python script to control Linak standing desks.
MIT License
357 stars 50 forks source link

DPG1C Support #32

Closed linusbierhoff closed 1 year ago

linusbierhoff commented 2 years ago

Hi! I am not able to move my desk. When I try to run e.g. idasen-controller --sit my raspberry pi connects and gets the right height but nothing happens:

Connected D7:78:43:EE:CF:34 Height: 885mm Timed out while waiting for desk Final height: 885mm (Target: 683mm) Disconnected

Is there anything I need to be aware of when using the script on a raspberry pi or with a linak desk?

rhyst commented 2 years ago

I only have a Idasen desk and I think the other Linak controllers use different commands so the scope of this tool was just for the Linak desk.

If you were able to find out what commands work on the DPG1C then it's possible that I could include them in the script.

rhyst commented 2 years ago

If you want to do this you can run this script using the Bleak python library https://github.com/hbldh/bleak/blob/develop/examples/service_explorer.py

If you were able to post the output here then we could try to work out what the correct characteristics or commands are for the DPG1C

vniehues commented 2 years ago

@linusbierhoff another option (if you have a Mac) would be Bluetillity. You can just select the desk and browse every service and characteristic to provide them here.

rhyst commented 2 years ago

I also remembered that someone else apparently got the DPG1C working and claimed it used the same commands: https://github.com/rhyst/idasen-controller/issues/3

dmitrym0 commented 2 years ago

I'm having trouble wit this as well. After reading #3, I reset the controller per the manual and re-paired it. But it only seems to be reading the status and not settings it:

~/w  ❯❯❯ idasen-controller --mac-address 4520FE33-3754-4E70-BF61-55D342263FE3 --move-to 910               main ⬆ ◼
Connected 4520FE33-3754-4E70-BF61-55D342263FE3
Height:  902mm
Timed out while waiting for desk
Final height:  902mm (Target:  910mm)
Disconnected
dmitrym0 commented 2 years ago

Here's the output from the serviceexplorer script linked above: https://gist.github.com/dmitrym0/7394da30f2e543b5d4ac825a604d989b

kabakakao commented 2 years ago

I can confirm, that getting the status is working, but not setting a position

rhyst commented 2 years ago

@dmitrym0 thank you for getting that.

It's odd, it looks like it has all the right characteristics. The only thing I can think of is that for some reason that notifications on the height characteristic aren't working on the DPG1C.

If you are comfortable running this as a python script then some things that would be useful to try are:

I am currently away so limited to what I can try. One thing I will add when I am back at my desk is some better logging so you don't have to fiddle with the script if you don't want to.

dmitrym0 commented 2 years ago

I tried running locally with the following values:

#UUID_HEIGHT = '99fa0003-338a-1024-8a49-009c0215f78a'
#UUID_HEIGHT = '99fa0022-338a-1024-8a49-009c0215f78a'
#UUID_HEIGHT = '99fa0023-338a-1024-8a49-009c0215f78a'
#UUID_HEIGHT = '99fa0024-338a-1024-8a49-009c0215f78a'
#UUID_HEIGHT = '99fa0025-338a-1024-8a49-009c0215f78a'
#UUID_HEIGHT = '99fa0026-338a-1024-8a49-009c0215f78a'
#UUID_HEIGHT = '99fa0027-338a-1024-8a49-009c0215f78a'
#UUID_HEIGHT = '99fa0028-338a-1024-8a49-009c0215f78a'

No change.

I also instrumented the code with some prints; it looks like _move_to is not being called at all:

Connected 4520FE33-3754-4E70-BF61-55D342263FE3
Height:  855mm
move_to
move_to after height
about to subscribe
got height
Moving up
move_up
Timed out while waiting for desk
Final height:  855mm (Target:  900mm)
Disconnected

So move_up from the log above is this function. Once the desk start moving, we expect _move_to to get called?

If move_up doesn't start the movement, then I guess it makes sense that _move_to doesn't get called.

rhyst commented 2 years ago

If you run the script with --monitor and then move the desk using the physical switch, do you see the height being updated?

dmitrym0 commented 2 years ago

Yup:

~/w/g/idasen-controller ❯❯❯ python3 idasen_controller/main.py --mac-address 4520FE33-3754-4E70-BF61-55D342263FE3 --monitor                                                                                                                                     ✘ 130 master ✱
Connected 4520FE33-3754-4E70-BF61-55D342263FE3
Height: 1281mm
Height: 1281mm Speed: -4mm/s
Height: 1281mm Speed: -7mm/s
Height: 1280mm Speed: -1mm/s
rhyst commented 2 years ago

Aha, that must means that the movement commands are wrong then.

Apologies for the trial and error style approach but could you try replacing UUID_COMMAND with the different characteristics that you found earlier? I would imagine it would be one of the ones labelled: write-without-response,write

If that doesn't work then it might be that we have the right characteristic but we are sending the wrong data. In which case I'll probably have to get the android app and decompile it again to have a look.

kabakakao commented 2 years ago

I had a small success. I opened the iOS App and allowed the app to allow movement in both directions. After that the movement works but just for a few steps

rhyst commented 2 years ago

Ah that is interesting because in the decompiled app there is some code for sending a wakeup command. I wonder if that explains why this works intermittently.

I think the command could be defined as:

COMMAND_WAKEUP = bytearray(struct.pack("<H", 254))

And then send it just before we send the first movement command:

    # Listen for changes to desk height and send first move command (if we are 
    # not already at the target height).
    if not has_reached_target(initial_height, target):
        await subscribe(client, UUID_HEIGHT, _move_to)
        await client.write_gatt_char(UUID_COMMAND, COMMAND_WAKEUP )
        await client.write_gatt_char(UUID_COMMAND, COMMAND_STOP)
        if direction == "UP":
            asyncio.create_task(move_up(client))

(The app also sends a stop command immediately after wakeup so 🤷‍♂️ )

voruti commented 2 years ago

I got the same/a similar problem. The program is able to read all the values from the desk, but not able to trigger a movement. I'm using the DPG1M on an Inwerk Masterlift 2. Interestingly, everything worked a few days ago. Unfortunately, I really do not know what I have changed since then :disappointed:.

I added a print(...) before all client.write_gatt_char(...) and it looks like they get called when they should. The program is sending (for example) a COMMAND_UP and then waiting, while nothing happens at all. Now when I manually raise the table, idasen-controller detects this and starts to count (global count). Once it reaches 6, it sends another COMMAND_UP - and the desk stops moving :laughing::

Log (ignore my strange log output in between if necessary) ``` > python3 idasen_controller/main.py --config config.yaml --stand Connected 12:34:56:78:9A:BC Height: 749mm after subscribe after wakeup creating up: created up trying wait move done: raw send up: 99fa0002-338a-1024-8a49-009c0215f78a bytearray(b'G\x00') ``` Nothing happens; I move the desk up manually ``` new count: 1 Height: 749mm Target: 1156mm Speed: 6mm/s new count: 2 Height: 749mm Target: 1156mm Speed: 8mm/s new count: 3 Height: 750mm Target: 1156mm Speed: 11mm/s new count: 4 Height: 750mm Target: 1156mm Speed: 14mm/s new count: 5 Height: 750mm Target: 1156mm Speed: 16mm/s new count: 6 Height: 751mm Target: 1156mm Speed: 20mm/s raw send up: 99fa0002-338a-1024-8a49-009c0215f78a bytearray(b'G\x00') new count: 1 Height: 752mm Target: 1156mm Speed: 23mm/s new count: 2 Height: 752mm Target: 1156mm Speed: 25mm/s new count: 3 Height: 754mm Target: 1156mm Speed: 28mm/s new count: 4 Height: 754mm Target: 1156mm Speed: 21mm/s new count: 5 Height: 755mm Target: 1156mm Speed: 13mm/s new count: 6 Height: 755mm Target: 1156mm Speed: 9mm/s raw send up: 99fa0002-338a-1024-8a49-009c0215f78a bytearray(b'G\x00') new count: 1 Height: 755mm Target: 1156mm Speed: 5mm/s new count: 2 Height: 755mm Target: 1156mm Speed: 0mm/s raw send stop waited for done raw send reference_stop Final height: 755mm (Target: 1156mm) raw send stop raw send reference_stop Disconnected ``` (If you look closely at the speed, you can see that the `up` command stops the desk.)

I also tried this

sending a wakeup command

with and without the following stop command, but it seems to have no effect. (To make it clear: My desk does not move for a few steps like @kabakakao described, but stays completely still. I tried the wakeup command anyway.)


My `service_explorer.py` for completeness ``` INFO:__main__:Connected: True INFO:__main__:[Service] 00001801-0000-1000-8000-00805f9b34fb (Handle: 8): Generic Attribute Profile INFO:__main__: [Characteristic] 00002a05-0000-1000-8000-00805f9b34fb (Handle: 9): Service Changed (indicate), Value: None INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 11): Client Characteristic Configuration) | Value: b'\x02\x00' INFO:__main__:[Service] 99fa0001-338a-1024-8a49-009c0215f78a (Handle: 12): Unknown INFO:__main__: [Characteristic] 99fa0002-338a-1024-8a49-009c0215f78a (Handle: 13): Unknown (write-without-response,write), Value: None INFO:__main__: [Characteristic] 99fa0003-338a-1024-8a49-009c0215f78a (Handle: 15): Unknown (read,notify), Value: b'' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 17): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__:[Service] 99fa0010-338a-1024-8a49-009c0215f78a (Handle: 18): Unknown INFO:__main__: [Characteristic] 99fa0011-338a-1024-8a49-009c0215f78a (Handle: 19): Unknown (read,write-without-response,write,notify), Value: b'\x01\x02\xa3\x0c' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 21): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__:[Service] 0000180a-0000-1000-8000-00805f9b34fb (Handle: 22): Device Information INFO:__main__: [Characteristic] 00002a29-0000-1000-8000-00805f9b34fb (Handle: 23): Manufacturer Name String (read), Value: b'LINAK A/S' INFO:__main__: [Characteristic] 00002a24-0000-1000-8000-00805f9b34fb (Handle: 25): Model Number String (read), Value: b'DPG' INFO:__main__:[Service] 99fa0020-338a-1024-8a49-009c0215f78a (Handle: 27): Unknown INFO:__main__: [Characteristic] 99fa0021-338a-1024-8a49-009c0215f78a (Handle: 28): Unknown (read,notify), Value: b'\xc6\x04\x00\x00' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 30): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__: [Characteristic] 99fa0022-338a-1024-8a49-009c0215f78a (Handle: 31): Unknown (read,notify), Value: b'\x00\x00\x01\x00' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 33): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__: [Characteristic] 99fa0023-338a-1024-8a49-009c0215f78a (Handle: 34): Unknown (read,notify), Value: b'\x00\x00\x01\x00' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 36): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__: [Characteristic] 99fa0024-338a-1024-8a49-009c0215f78a (Handle: 37): Unknown (read,notify), Value: b'\x00\x00\x01\x00' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 39): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__: [Characteristic] 99fa0025-338a-1024-8a49-009c0215f78a (Handle: 40): Unknown (read,notify), Value: b'\x00\x00\x01\x00' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 42): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__: [Characteristic] 99fa0026-338a-1024-8a49-009c0215f78a (Handle: 43): Unknown (read,notify), Value: b'\x00\x00\x01\x00' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 45): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__: [Characteristic] 99fa0027-338a-1024-8a49-009c0215f78a (Handle: 46): Unknown (read,notify), Value: b'\x00\x00\x01\x00' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 48): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__: [Characteristic] 99fa0028-338a-1024-8a49-009c0215f78a (Handle: 49): Unknown (read,notify), Value: b'\x00\x00\x01\x00' INFO:__main__: [Descriptor] 00002902-0000-1000-8000-00805f9b34fb (Handle: 51): Client Characteristic Configuration) | Value: b'\x00\x00' INFO:__main__: [Characteristic] 99fa0029-338a-1024-8a49-009c0215f78a (Handle: 52): Unknown (read), Value: b'\x01' INFO:__main__: [Characteristic] 99fa002a-338a-1024-8a49-009c0215f78a (Handle: 54): Unknown (read), Value: b'\x01' INFO:__main__:[Service] 99fa0030-338a-1024-8a49-009c0215f78a (Handle: 56): Unknown INFO:__main__: [Characteristic] 99fa0031-338a-1024-8a49-009c0215f78a (Handle: 57): Unknown (write-without-response,write), Value: None INFO:__main__: [Characteristic] 99fa0032-338a-1024-8a49-009c0215f78a (Handle: 59): Unknown (write-without-response,write), Value: None INFO:__main__: [Characteristic] 99fa0033-338a-1024-8a49-009c0215f78a (Handle: 61): Unknown (write-without-response,write), Value: None INFO:__main__: [Characteristic] 99fa0034-338a-1024-8a49-009c0215f78a (Handle: 63): Unknown (write-without-response,write), Value: None ```
voruti commented 2 years ago

I thought maybe the up command for my desk is using another number than 71 and did this:

async def move_up(client):
    print('raw send up with loop:', UUID_COMMAND)
    for i in range(270):
        print(i)
        await client.write_gatt_char(UUID_COMMAND, bytearray(struct.pack("<H", i)))
        time.sleep(1)
    print('up loop finsihed', UUID_COMMAND)

but nothing happened :smile:.

rhyst commented 2 years ago

So one thing I realised was that maybe the wake up command is being sent to the "reference input" characterisitc rather than the "command" characteristic. Perhaps someone could try:

    # Listen for changes to desk height and send first move command (if we are 
    # not already at the target height).
    if not has_reached_target(initial_height, target):
        await subscribe(client, UUID_HEIGHT, _move_to)
        COMMAND_WAKEUP = bytearray(struct.pack("<H", 254))
        await client.write_gatt_char(UUID_REFERENCE_INPUT, COMMAND_WAKEUP )
        await client.write_gatt_char(UUID_COMMAND, COMMAND_STOP)
        if direction == "UP":
            asyncio.create_task(move_up(client))
Nepomucene commented 2 years ago

Made changes above, and tried a "move-to" followed by a "sit". After the "sit", I now have the following error (Note that I'll have to wait a couple of hours to see if the wakeup worked)

Height: 733mm Target: 730mm Speed: -34mm/s Height: 732mm Target: 730mm Speed: -26mm/s Height: 731mm Target: 730mm Speed: -17mm/s Task exception was never retrieved future: <Task finished coro=<unsubscribe() done, defined at /home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py:312> exception=BleakDBusError('org.bluez.Error.Failed', 'No notify session started')> Traceback (most recent call last): File "/home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py", line 314, in unsubscribe await client.stop_notify(uuid) File "/home/pi/.local/lib/python3.7/site-packages/bleak/backends/bluezdbus/client.py", line 963, in stop_notify assert_reply(reply) File "/home/pi/.local/lib/python3.7/site-packages/bleak/backends/bluezdbus/utils.py", line 23, in assert_reply raise BleakDBusError(reply.error_name, reply.body) bleak.exc.BleakDBusError: [org.bluez.Error.Failed] No notify session started Final height: 730mm (Target: 730mm) Disconnected

Nepomucene commented 2 years ago

~Left it overnight and tried this morning. Sadly no movement :-(~

Probably better if I add the command definition for WAKEUP 🤦‍♂️ But now need to wait a couple of hours for the controller to go back to sleep

Note that I have some bleak errors, so the previous fix is not fully working: Height: 733mm Target: 730mm Speed: -34mm/s Height: 732mm Target: 730mm Speed: -26mm/s Height: 731mm Target: 730mm Speed: -20mm/s Task exception was never retrieved future: <Task finished coro=<unsubscribe() done, defined at /home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py:313> exception=BleakDBusError('org.bluez.Error.Failed', 'No notify session started')> Traceback (most recent call last): File "/home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py", line 315, in unsubscribe await client.stop_notify(uuid) File "/home/pi/.local/lib/python3.7/site-packages/bleak/backends/bluezdbus/client.py", line 963, in stop_notify assert_reply(reply) File "/home/pi/.local/lib/python3.7/site-packages/bleak/backends/bluezdbus/utils.py", line 23, in assert_reply raise BleakDBusError(reply.error_name, reply.body) bleak.exc.BleakDBusError: [org.bluez.Error.Failed] No notify session started Final height: 730mm (Target: 730mm) Disconnected

Nepomucene commented 2 years ago

Sadly after 4h, the controller went to sleep, and I tried again. No result. I have added a few prints here and there, and the "WAKEUP" and "STOP" are sent, but no action.

My question would be how confident are you with the exact WAKEUP command? (Both "UUID_REFERENCE_INPUT" and "COMMAND_WAKEUP") Note that you already seem to be sending the WAKEUP in the "move_up" and "move_down" so the addition here (https://github.com/rhyst/idasen-controller/issues/32#issuecomment-1046068950) may not be essential

Nepomucene commented 2 years ago

Played with the WAKEUP command:

But also interestingly, tried just launching the app (no desk movement) and killing it (to close the bluetooth connection) instantly. Commands instantly worked.

The wakeup is at app opening, not when sending up/down instructions (maybe also there)

rhyst commented 2 years ago

I've just pushed a change which may help with this if anyone is willing to test.

I was inspired by this repo to use a different characteristic to perform the height commands. As mentioned in that repo its essentially a "move to" command rather than a "move up/down" command which means that the desk now stops dead on the target height.

This works for me perfectly but I found I needed to add the wake up command, even on the Linak desk, and as it works on my desk I think that means I have got the wakeup command working. Maybe this will work on the DPC1C?

Change is only on github, not on pip yet.

vniehues commented 2 years ago

If somebody gets it to work with the DPC1C now, please report back! I'd love to add this to my homebridge plugin.

dmitrym0 commented 2 years ago

Just updated to the latest master, wanted to give this a go, but I can't seem to connect to the desk anymore. Do I use the Device identifier, in Bluetility on a Mac?

image image

The desk appears to be paired:

image

I don't recall having these issues previously.

rhyst commented 2 years ago

Nothing has changed with the initial connection so it shouldn't be different 😓

Have you tried increasing the connection timeout? And maybe unpair and repair?

vniehues commented 2 years ago

@dmitrym0 did you recently update to macOS Monterey? If yes, the problem might be related to #33

dmitrym0 commented 2 years ago

@dmitrym0 did you recently update to macOS Monterey? If yes, the problem might be related to #33

Ahh you're bang on. Updated a couple of weeks ago. Thanks for pointing that out.

Nepomucene commented 2 years ago

Sadly not sure of how to update to test if it is not on pip (user limitations, sorry) I have updated main.py, but there are apparently other changes...

rhyst commented 2 years ago

Ah I've uploaded it as 1.1.0rc1 so you should be able to run:

pip install idasen-controller==1.1.0rc1
Nepomucene commented 2 years ago

Connecting, but no movement :-( _pi@Pi-Hole:~ $ idasen-controller --stand Connected FF:11:18:9A:56:13 Height: 812mm Height: 812mm Speed: 0mm/s Disconnected
Traceback (most recent call last): File "/home/pi/.local/bin/idasen-controller", line 10, in sys.exit(init()) File "/home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py", line 499, in init asyncio.run(main()) File "/usr/lib/python3.7/asyncio/runners.py", line 43, in run return loop.run_until_complete(main) File "/usr/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete return future.result() File "/home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py", line 485, in main await run_command(client, config, print) File "/home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py", line 390, in run_command await move_to(client, target, log=log) File "/home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py", line 332, in move_to await unsubscribe(client, UUID_HEIGHT) File "/home/pi/.local/lib/python3.7/site-packages/idasen_controller/main.py", line 301, in unsubscribe await client.stop_notify(uuid) File "/home/pi/.local/lib/python3.7/site-packages/bleak/backends/bluezdbus/client.py", line 963, in stop_notify assert_reply(reply) File "/home/pi/.local/lib/python3.7/site-packages/bleak/backends/bluezdbus/utils.py", line 23, in assert_reply raise BleakDBusError(reply.errorname, reply.body) bleak.exc.BleakDBusError: [org.bluez.Error.Failed] No notify session started

JulianBastel commented 2 years ago

Do you know about: https://github.com/anson-vandoren/linak-desk-spec The person is reengineering the linak android 'Desk Control app' and figured out theres is some handshake required between the desk and the app.

I also have a DPG1M on an Inwerk Masterlift 2.

I used my raspi 3 b+ of the 3d printer and rasp OS and I got the it bit working with (BleakClient). I order a raspi 4 from polin to continue the development.

Goal is to use the lib for integration in Home Assistant.

patrykstaskiewicz commented 2 years ago

After a few evenings of trying to integrate my DPG1C controller with my HP-T620 server (and Homebridge) it works but with one limitation. Occasionally controller from standby goes to some kind of deep sleep mode and stop working with Home bridge or CLI. Only things that wake up controller is connecting via app; after that all function works.

Don't sure if before connecting controller required some kind of handshake or wake up command but surely one of these

rhyst commented 2 years ago

That is a very interesting repo, thank you for finding it! It's funny that so many people have done roughly the same thing by trying to decompile and reverse engineer the Linak app 😆

From what I can see this person has deduced that the app normally sends a series of commands to work out the capabilities of the desk. This is not really an explicit wakeup command but maybe the desk expects it?

For my own reference at least I'm going to try to summarise the findings of that repo. According to them the app does the following:

I've ignored the desk responses because I think its just the app gathering info and the actual response does not affect any of the following commands.

More simply this seems to be:

So not super clear if that corresponds to a wake up sequence and not clear if all of it is necessary but it's worth a try to simply send all of those commands I think. Possibly sticking them in the wakeUp function in this repo would be a good way to try it out.

I am unfortunately away from my Linak desk so won't be able to test anything for a few months, but I would encourage anyone else to modify the code here and try it.

tomsb commented 1 year ago

Exactly how to stick those commands into wakeUp function? I guess this is not the right way:

async def wakeUp(client):
    await client.write_gatt_char(UUID_COMMAND, "0x7F-86-00")
    await client.write_gatt_char(UUID_COMMAND, COMMAND_WAKEUP)

@rhyst I am willing to test using trial and error style approach. Could you give me wakeUp function sample code?

rhyst commented 1 year ago

I think that's basically correct though I think you need to format the bytes like this:

await client.write_gatt_char(UUID_COMMAND, b"\x7F\x86\x00")

I did not really get my head round the byte format last time I played with it so that may not be correct either, but I am pretty sure you will need to use a python bytes object which is what the b"" does.

My first attempt would probably be to just write all the commands listed in my comment above (including the user id) one after another in the wakeup command.

tomsb commented 1 year ago

I am no familiar with python and don't know how to format the bytes. Tried this and nothing happens:

async def wakeUp(client):
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x80")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x86\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x81\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x88\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x89\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x8a\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x8b\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x8c\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x8A\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x8B\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x8C\x00")
    await client.write_gatt_char(UUID_COMMAND, b"\x7F\x86\x80\x10\xe5\xc0\xca\xd8\xbe\xc4\x48\xe1\xa0\x80\x3e\x56\xf8\xd4\xcf\xca")
    await client.write_gatt_char(UUID_COMMAND, COMMAND_WAKEUP)

Any other ideas? P.S. I got the desk moving using usb2lin06 device: https://github.com/monofox/python-linak-desk-control Maybe this somehow helps? (I'm not sure how to interpret/convert the bytes used in their source)

datbilling commented 1 year ago

I have the same issue and also found it only works after first connecting with the ios app and moving the desk. Is there any progress on this or any way I may be able to help?

rhyst commented 1 year ago

I do not think there has been any progress beyond @tomsb trying the commands I suggested. Sorry for my delayed response as well.

I think the next step I would try is to more closely emulate the decompiled app. I.e. maybe it requires the notification to be received, or maybe it requires them to be send before it can receive the next command.

LuisDiazUgena commented 1 year ago

Hi,

I have a linak desk with DPG1C controll unit and would love to help to identify what is going on here.

Anything I can do to help?

rhyst commented 1 year ago

Hey, really all it needs is someone who has DPG1C who is comfortable hacking away at the python script (that's all I did to make it for the Linak after all).

I think progress could be made by trial and erroring the commands sent to the desk. What I remember from playing around with the decompiled official app was that there's a lot of shared code and the desks should behave similarly and I expect it's just missing some initialisation or something.

Slightly more advanced would be trying to listen to what commands are sent between the official app and the desk.

rhyst commented 1 year ago

Also seeing if other projects have solved this. There's quite a few different desk controlling scripts and apps around.

LuisDiazUgena commented 1 year ago

Thanks for your quick answer rhyst.

For me it's not a problem to tinker with hardware and software at all. Been doing that for a couple of years now. Sorry, i didn't point that out in my first message, was a long day after a long week after a long month and I was really sleepy.

I'm going to download the repo and start tinkering with that in order to get familiarized with it, also, will try to sniff the bluetooth comunication with the official app. Will reach soon (I hope).

There is no community to chat about develop this, right?

LuisDiazUgena commented 1 year ago

Hi @rhyst,

I have sniffed the trafic on the app and opened in wireshark. I have logged one up and one down operation. The file opened in wireshark looks like this:

Captura de pantalla 2023-04-03 162943

Still need to dig into the repo code to see how can it be merged or used in it, but it might be useful to you as it is.

btsnoop_hci_20230403_102322.zip

AhmedKamal commented 1 year ago

I just got DL6 Linak (DPG1C I think) and I was able to make it work perfectly with this, only issue with the privacy problem with macos and had to set the base height and allowed range properly in the config.

LuisDiazUgena commented 1 year ago

@AhmedKamal If you connect the desk to the app and then to the command line interface, the desk will work properly. Does it work for you later on?

mensfeld commented 1 year ago

It seems to be unlocked if you rename the Bluetooth device though I still don't know how to do it via commands.

AhmedKamal commented 1 year ago

@LuisDiazUgena Yea you are right, it stopped working after that, the CLI is able to get the desk position but it doesn't move in reality, not sure why !

➜  ~ idasen-controller --move-to 900

Connected C22F3DAB-0E90-824C-D645-D5C50AC994B4
Height:  657mm
Moving to height: 900
Height:  657mm Speed:  0mm/s
Final height:  657mm (Target:  900mm)
Disconnected
mensfeld commented 1 year ago

@AhmedKamal that is because our desks require additional command after the device goes to sleep. It seems that any accepted write to settings or desk rename does the job (tested with the linak app) but so far I was not able to reverse engineer the actual command

AhmedKamal commented 1 year ago

Yea, I tried to connect from the phone and then back and then it worked again. I found a paid app that is able to control the desk which means it is feasible. https://apps.apple.com/us/app/desk-remote-control/id1509037746

I'm not sure how to capture the Bluetooth communication. It has a macos app as well.

@mensfeld @LuisDiazUgena

mensfeld commented 1 year ago

@AhmedKamal unfortunately for me it's mac and I'm linux. I have the BLE communication of the original app from the android phone as well as I was able to decompile the android app but the complexity of the update code makes it hard for me to figure out :(