Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support DSP-W115? #1

Closed
Garfonso opened this issue May 13, 2020 · 15 comments
Closed

Support DSP-W115? #1

Garfonso opened this issue May 13, 2020 · 15 comments

Comments

@Garfonso
Copy link

Garfonso commented May 13, 2020

Hi,

I have a DSP-W115 which I would like to control without using the mydlink cloud.

I saw the comment in your readme that support of DSP-W115 might work, so I guessed I'd give it a try.
Sadly it did not work out of the box. I receive the following log/error (I added the output of the token, in order to see if it was set at all):

Sending HTTP upgrade request
Sending sign_in to get salt
Got token: 7c06be9ae400562c45e784e26eb79b54e95d365b
Sending get device status
Error (424): invalid device token

Do you have any hints on what to try?
Can I help you in any way?

May I ask you how you found out to control the device in this way and on what protocol that is based?
I worked with the older DLink Switches DSP-W215 with the HNAP1 interface, but sadly that does not seem to be present on the DSP-W115 anymore (or altered to a point where I can not use it, because it always rejects the login action).

//Edit:
I tried to open https://IP:8080/ in my webbrowser and it failed with the error ERR_BAD_SSL_CLIENT_AUTH_CERT if that is of any help. But that does not seem to be an issue with the python script? This is a bit over my head. 😄

//Edit2:
Ah, understood how to get more verbose logoutput. Here is with received messages:

Sending HTTP upgrade request
Recieved:
"b'HTTP/1.1 101 Switching Protocols\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: Kfh9QIsMVZcl6xEPYxPHzW8SZ8w=\r\n\r\n'"

Sending sign_in to get salt
Recieved:
"{ "client_id": "", "command": "sign_in", "sequence_id": 1001, "local_cid": 35324, "timestamp": 1589386348, "salt": "25f1e1513011616b6bec7813dcb49907", "init": 1, "code": 0, "message": "no error", "device_id": "180F76CC6C09" }"

Sending get device status
Recieved:
"{ "local_cid": 35324, "device_id": "180F76CC6C09", "device_token": "180F76CC6C09-03b34f24e8b90ecbd15fe06f3dc566f0a08b842f", "command": "device_status", "sequence_id": 1001, "client_id": "", "timestamp": 1589386348, "salt": "25f1e1513011616b6bec7813dcb49907", "init": 1, "code": 424, "message": "invalid device token" }"

Error (424): invalid device token
@jonassjoh
Copy link
Owner

jonassjoh commented May 13, 2020

Based on the output it appears as if your device is paired with the mydlink app. Is this correct?

If so, have you aquired the device token that is stored on the device itself? If you have, you should replace the line pin = "000000" with pin = "<DEVICE TOKEN>". I'm afraid that the token is required should you wish to have it remain connected to the mydlink app.

@jonassjoh
Copy link
Owner

Some other points.

Can I help you in any way?

If you find that the program works for your device I'd be interested to know. If you create any improvements to the program at all, I'll be more than happy to include them.

May I ask you how you found out to control the device in this way and on what protocol that is based?

I reverse engineered the binary responsible for the server found on port 8080 and 8081 (the binary is called da_adaptor). As far as I can tell they are using their own protocol. Although part of the traffic is parsed using HybiParser (as can be seen in the code). The HNAP1 protocol is no longer in use and will respond with generic messages to all request such as "This command does not exist" or something similar (I cannot remember the exact wording).

ERR_BAD_SSL_CLIENT_AUTH_CERT

Your computer holds a set of trusted certificates when connecting to SSL services. The certificate used by the plug is not covered by this list, as such the browser will not connect to it, although this is not an issue with the script.

I tried to open https://IP:8080/ in my webbrowser

There is no graphical web interface.

@Garfonso
Copy link
Author

Thanks for the fast response.

Based on the output it appears as if your device is paired with the mydlink app. Is this correct?

Yes, this is the case. Was this a mistake?

If so, have you aquired the device token that is stored on the device itself? If you have, you should replace the line pin = "000000" with pin = "<DEVICE TOKEN>". I'm afraid that the token is required should you wish to have it remain connected to the mydlink app.

How can I aquire the device token? Can I see it in the app somewhere?
I tried with the PIN on the back of the device before, but that obviously was wrong.
Can I use the PIN=000000 if I reset the device to factory settings (but how to add it to my wifi, then?) - I do not really care for the app.

I will sure let you know if I get it working.
Thanks for the additional information. 😃

@jonassjoh
Copy link
Owner

jonassjoh commented May 13, 2020

Have a look at the README under "Usage", if you have any questions about the instructions there, just ask.

How can I aquire the device token? Can I see it in the app somewhere?

As far as I know you can only get this from the device itself. Although it's not impossible that it can be obtained through D-Link in some way, I'm just not aware as to how.

@Garfonso
Copy link
Author

Uhhh... sorry, I must have missed that part about the device ID when skimming through Usage earlier.

Ok, I now got some results.
For improvements on Usage, one might add for the factory reset method that, at least on DPS-W115 one can press the reset button until the device starts to blink red (on normal boot after a reset it will blink orange).

Then I tried it without setting it up in the app (I did not do the last step in the app after the DSP-W115 connected to my wifi) with Pin=000000. That did not work. Did I understand it correctly that it should work in that way (or works on the DSP-W245)?

Then I completed the setup and extracted the device_token with that I can talk to the DSP-W115, switch the socket and enable / disable the LED (but both commands set_socket and set_led to both actions at the same time, is that correct?)

I get some exceptions, though. On keepAlive and on set_socket(1, True/False) I get this error:

Sending keep alive
Recieved:
"{ "command": "event", "sequence_id": 1589393426, "timestamp": 1589393426, "push_event": 0, "event": { "type": 61, "name": "Setting Change", "timestamp": 1589393426046973, "device_id": "180F76CC6C09", "mydlink_no": "37992507", "metadata": { "value": 1, "uid": 0, "type": 16, "idx": 0 } } }"

Traceback (most recent call last):
  File "test.py", line 17, in <module>
    sp.keep_alive()
  File "/home/pi/switchbot/dspW245/dspW245.py", line 343, in keep_alive
    self.send_json(data, log="Sending keep alive")
  File "/home/pi/switchbot/dspW245/dspW245.py", line 247, in send_json
    if r['code'] != 0:
KeyError: 'code'

@jonassjoh
Copy link
Owner

jonassjoh commented May 13, 2020

I agree that the instructions could be made a bit more clear.

with Pin=000000

The pin code is always what it says on the back of the device (so it should not be 000000). It will not change if you reset the device. Other than that it should work as long as the device is connected to the WiFi but not the app.

both commands set_socket and set_led to both actions at the same time, is that correct?

Yes. They are identical.

I get some exceptions, though.

Hmm. I'm not sure what's causing the error. It could be due to the models being slighly different. The error handling is mostly put in place to detect issues when setting everything up, so you could potentially remove lines 247 to 249 (including) to get around it as the communication looks ok otherwise(?).

@Garfonso
Copy link
Author

Yes, communication was good already. Without the lines you mentioned the exception goes away and I can switch the socket multiple times. Great work.

Thank you very much for your support. So you can report the dsp W115 working, I'd say.

@jonassjoh
Copy link
Owner

Glad to hear you got it working!

@Garfonso
Copy link
Author

I found out a bit more, maybe you are interested in that, too. I am no python developer and low on sparetime, too, so I won't augment this library... would take me too long and I'd make to many mistakes. My goal is to add support for those plugs in ioBroker, which is a node.js based smarthome middleware. So I build a node.js library from what you found out here: https://github.com/Garfonso/dlinkWebSocketClient

I noticed that the device token (i.e. what you need if connected to the app) changes on every reboot... luckily the telnet stays open, so you can easily get it again. But that is not optimal. I have to dig a bit more, there...

Also I added the function get_settings with just the "type": 16 parameter it returns the current settings (for DSP-W115 I get an array with one entry, so I assume for DSP-W245 you'll get the state of all four sockets? Maybe you can give that a try. But maybe you hate to supply it an array with multiple settings and get as many back? Not sure. I do not need to supply the index in get_setting, though).
Then I found out that you can indeed switch the LED on/off by using "type": 41 in set_setting / get_setting commands.

Thank you very much for all the work done which I could build upon.

@jonassjoh
Copy link
Owner

jonassjoh commented May 17, 2020

Yea I noticed your library by pure chance 2 minutes after you made a commit to it. I can't tell you how glad I am that you found my library useful!

Interesting. I was aware that the token changed when the device was reset, but I was not aware that it changed during normal reboots. I rarley rebooted my device, however, so it's possible I never ran into that.
Part of my project was to try and get around the authentication process of the device (through buffer overflows or what have you), which is not possible as far as I understand it.
The only way around it that I can think of is to replace the firmware of the device to one where this is not an issue. The upgrade_fw can do this and the firmware is not verified by the device in any way. There is, of course, a real risk of bricking the device however.
Another solution would be to use the telnet server to create the device, if I remember correctly you can control it from the terminal using:
GpioForCrond <S> <O> where <S> - 0=off, 1=on and <O> - 11/17/19/18 (outlet 1/2/3/4) (you may have to use different values for <O> on the W115).

There are a bunch of extra commands you can send the device. Since I had no use for any of the other commands, I never bothered to implement them. Initially I was never planning on releasing this library but I thought that someone might get some use out of it so I just released it more or less as it was.
I'm a tad surprised I never implemented the one you mentioned, however, but I'm glad you figured it out yourself. It sounds like a feature people might like.
I'm afraid I will not be implemening any new features myself since I do not have access to the device for much longer and won't be able to test any new code.

If you have any questions about the device itself (architecture, code, authentication, etc.) feel free to ask while my memory of it is still somewhat fresh.

@Garfonso
Copy link
Author

There are a bunch of extra commands you can send the device. Since I had no use for any of the other commands, I never bothered to implement them.

Do you happen to have a list or something? (I tried to dig a bit, but my knowledge in this area is pretty restricted. I basically only had a look at the string constants in the da_adaptor binary - but it is next to impossible to determine the parameters that way).
I'd be interested in how you found out so much about the device. Maybe you want to do a blogpost or something about it. ;-)

@jonassjoh
Copy link
Owner

Here is a list of possible commands that you can send. I'm not sure if they will all work, but they are present in the code atleast. I also noticed that set_fw_auto_upgrade is present in the list, which might be of interest to you considering your other comment.

sign_in
av_config
get_setting
set_setting
status
get_status
start_viewing
stop_viewing
register
recycle_channel
get_policy
get_schedule
change_policy
change_schedule
change_scene
event
unbind_device
update_info
device_reset
reload_config
get_server_info
set_service_level
get_service_level
keep_alive
upload_log
voice_report
get_unit_data
get_setup_status
wlan_survey
test_token
get_regular
conn_init
conn_add_candidate
rtsp_command
rtsp_start_viewing
conn_close
conn_list
device_status
update_cnvr_state
sd_list_event
sd_list_snapshot
sd_list_file
fw_upgrade
m2m_event
rtsp_ack_channel
set_fw_auto_upgrade
get_fw_auto_upgrade
change_policy_batch
change_schedule_batch
change_scene_batch

@jonassjoh
Copy link
Owner

I used Ghidra to reverse engineer the binary. It was a slow process since it was the first time I did something like this. Ghidra will provide an approximate decompilation of the binary in C, which helped a lot.

The main function of interest that handles sent requests (after upgrading from HTTP to the JSON based protocol) is called da_svr_recv_pkt. The function will perform the authentication using the pin/devicetoken etc.
The possible commands that can be sent are present in the function sxc_json_command_no.

@Garfonso
Copy link
Author

Do you maybe happen to know a function that returns the model of the device? Could not find anything...

@jonassjoh
Copy link
Owner

jonassjoh commented May 30, 2020

You could use the webserver present on the device http://192.168.0.20/login?username=Admin&password=PIN. The user name is "Admin" (case sensitive) and the password is your pin code.
When you login you are presented with some static information about the device. The information you would be interested in is the 2.4GHz SSID, it should look something like this:

2.4GHz SSID: DSP-W245-0000

If I remember correctly the last four characters are from the MAC address, so you could just remove those.

I am unaware of any other way of getting the information without using telnet.
It is possible that the information is also sent back with some of the functions as you mentioned. You said that it was possible to get the states of the outlets returned as an array? I would've guessed that the information would be contained within the response since it's required to set the states.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants