idasen-controller icon indicating copy to clipboard operation
idasen-controller copied to clipboard

DPG1C Support

Open linusbierhoff opened this issue 3 years ago • 30 comments

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?

linusbierhoff avatar Jan 16 '22 00:01 linusbierhoff

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 avatar Jan 17 '22 10:01 rhyst

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

rhyst avatar Jan 17 '22 15:01 rhyst

@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.

vniehues avatar Jan 21 '22 13:01 vniehues

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

rhyst avatar Jan 21 '22 15:01 rhyst

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 avatar Jan 30 '22 22:01 dmitrym0

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

dmitrym0 avatar Jan 30 '22 22:01 dmitrym0

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

kabakakao avatar Feb 05 '22 18:02 kabakakao

@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:

  • There's a bunch of characteristics with : Unknown (read,notify) after them. Try replacing the value of UUID_HEIGHT with the UUID of those characteristics. Then run the script and see if it still reports the current height, and then if it moves.
  • You could try adding some print statements in the move_to and _move_to functions. It would be useful to know if it actually sends the initial move_up or move_down command and if the _move_to function is ever called.

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.

rhyst avatar Feb 07 '22 17:02 rhyst

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.

dmitrym0 avatar Feb 07 '22 22:02 dmitrym0

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

rhyst avatar Feb 08 '22 11:02 rhyst

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

dmitrym0 avatar Feb 08 '22 16:02 dmitrym0

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.

rhyst avatar Feb 08 '22 16:02 rhyst

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

kabakakao avatar Feb 08 '22 17:02 kabakakao

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 🤷‍♂️ )

rhyst avatar Feb 08 '22 17:02 rhyst

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 avatar Feb 09 '22 16:02 voruti

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:.

voruti avatar Feb 09 '22 16:02 voruti

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))

rhyst avatar Feb 19 '22 17:02 rhyst

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 avatar Feb 19 '22 20:02 Nepomucene

~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 avatar Feb 21 '22 10:02 Nepomucene

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 avatar Feb 21 '22 15:02 Nepomucene

Played with the WAKEUP command:

  • Tried different UUID (0031 to 0034)
  • Tried different payloads (1 to 270) All failed

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)

Nepomucene avatar Feb 21 '22 19:02 Nepomucene

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.

rhyst avatar Mar 07 '22 18:03 rhyst

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

vniehues avatar Mar 08 '22 09:03 vniehues

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.

dmitrym0 avatar Mar 08 '22 21:03 dmitrym0

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?

rhyst avatar Mar 08 '22 21:03 rhyst

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

vniehues avatar Mar 08 '22 21:03 vniehues

@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.

dmitrym0 avatar Mar 08 '22 22:03 dmitrym0

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...

Nepomucene avatar Mar 09 '22 14:03 Nepomucene

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

pip install idasen-controller==1.1.0rc1

rhyst avatar Mar 09 '22 15:03 rhyst

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.error_name, reply.body) bleak.exc.BleakDBusError: [org.bluez.Error.Failed] No notify session started

Nepomucene avatar Mar 09 '22 20:03 Nepomucene