Closed linusbierhoff closed 1 year 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.
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
@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.
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
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
Here's the output from the serviceexplorer script linked above: https://gist.github.com/dmitrym0/7394da30f2e543b5d4ac825a604d989b
I can confirm, that getting the status is working, but not setting a position
@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:
: 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.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.
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 print
s; 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.
If you run the script with --monitor
and then move the desk using the physical switch, do you see the height being updated?
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
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.
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
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 🤷♂️ )
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::
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.)
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:.
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))
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
~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
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
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)
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.
If somebody gets it to work with the DPC1C now, please report back! I'd love to add this to my homebridge plugin.
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?
The desk appears to be paired:
I don't recall having these issues previously.
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?
@dmitrym0 did you recently update to macOS Monterey? If yes, the problem might be related to #33
@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.
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...
Ah I've uploaded it as 1.1.0rc1
so you should be able to run:
pip install idasen-controller==1.1.0rc1
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
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.
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
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:
99fa0003-338a-1024-8a49-009c0215f78a
characteristic by writing to its 00002902-0000-1000-8000-00805f9b34fb
descriptor. In my notes I have this characteristic labelled as "Error" and its in the "Command" service so perhaps it's intended to report if there's an error with a command that is sent. 99fa0011-338a-1024-8a49-009c0215f78a
characteristic by writing to its 00002902-0000-1000-8000-00805f9b34fb
characteristic. I had this labelled as the "DPG" service and I think its likely that this is to do with desk configuration but I am unsure.99fa0029-338a-1024-8a49-009c0215f78a
characteristic. I have this labelled as "MASK" and its not clear what this is supposed to do. Apparently it returns 1
?0x7F-80
to DPG characteristic. Apparently this corresponds to the GET_CAPABILITIES
command. Desk responds in some way.99fa0021-338a-1024-8a49-009c0215f78a
characteristic. This is "REFERENCE_OUTPUT" characteristic and this is reading the current height.0x7F-86-00
to DPG characteristic. Apparently this is the USER_ID
command. Desk responds in some way.0x7f-81-00
to DPG characteristic. Apparently this is the DESK_OFFSET
command. Desk responds in some way.0x7f-88-00
to DPG characteristic. Apparently this is the REMINDER_SETTING
command. Desk responds in some way.0x7f-89-00
to DPG characteristic. Apparently this is the GET_SET_MEMORY_POSITION_1
command. Desk responds in some way.0x7f-8a-00
to DPG characteristic. Apparently this is the GET_SET_MEMORY_POSITION_2
command. Desk responds in some way.0x7f-8b-00
to DPG characteristic. Apparently this is the GET_SET_MEMORY_POSITION_3
command. Desk responds in some way.0x7f-8c-00
to DPG characteristic. Apparently this is the GET_SET_MEMORY_POSITION_4
command. Desk responds in some way.99fa0021-338a-1024-8a49-009c0215f78a
characteristic by writing to its 00002902-0000-1000-8000-00805f9b34fb
descriptor. This is the height notifications.0x7f-86-80-1-e5-c0-ca-d8-be-c4-48-e1-a0-8-3e-56-f8-d4-cf-ca
to the DPG characteristic. This is apparently the USER_ID
command again so perhaps now the app is setting the user id, rather than getting it? 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.
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?
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.
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)
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?
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.
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?
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.
Also seeing if other projects have solved this. There's quite a few different desk controlling scripts and apps around.
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?
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:
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.
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.
@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?
It seems to be unlocked if you rename the Bluetooth device though I still don't know how to do it via commands.
@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
@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
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
@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 :(
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:Is there anything I need to be aware of when using the script on a raspberry pi or with a linak desk?