Add Support For Proscenic Robot Vacuum Cleaners

As a help about it I can offer a python script, that I found on github, for controlling some basic operations and getting info from it.


As an additional help I can offer the full functionality http control links (I can try with the UDP packets too) when the cleaner arrives in a week

And additional topic exist too

2 Likes

Hi Ghost!

Thanks for your work on that robot (It is really hard to find people trying to integrate this vaccum in Domotics’ servers).

So far, this script is not working on my side, and I cannot understand why. I guess the new firmware/new app has blocked this functionnality from the previous version…

I also tried (like you) to analyse the Proscenic Robotic app, but it seems like all the code has been compiled within the app (in *.so and *.dll files within the apk).

For now, the only reverse engineering i performed is about wireshark (and other packet sniffers), showing some kind of Json exchange between the Proscenic server and the Robot. But I am, for now, not able to reproduce the exchange (through json, or even with packet sender, sending the hex data directly to the robot).

Have you investigated further more on the new version of the app/firmware?

Hi! When I reverse engineered the app, I saw the same like you, so I found one of the very first apps that contained the hex values. This way I built the plugin. I haven’t updated the vacuum firmware because I was afraid that the new version will block this useful, for me, functionality. Perhaps this is the reason why it doesn’t work for you. One thing I saw in the old app was the cloud server address and credentials for it for sending commands to it, and the server in other hand to send it to the vacuum cleaner, but I didn’t found anything about how to format the commands and send it to this server in order to make it work trough the cloud. :\

From what I sniffed, the hex data is sent through UDP protocol, and package seems to contain Json data. One information about the “cookie” on the other topic might be also the good way to change the data exchange from “local computer” <=> Robot instead http://bl-app-eu.robotbona.com <=> Robot.
I manage to construct what I guess is the Json data sent to the robot,but now I have to find a way to send it correctly to the robot…
EDIT: FYI: MCU: 1.8.2614(828) Wifi 1.0.41

I sniffed the first packages from starting the app. This is the post request answer that I can now get freely trough http request :slight_smile:
But after that they create a socket and start sending tcp packets with json content …

{“msg”:“ok”,“result”:“0”,“data”:{“deviceIp”:"",“openVoice”:“2”,“authCode”:“8554217”,“hadNoReadError”:“1”,“update”:{“isForce”:“0”,“isNeedUpdate”:“0”},“battery”:“100”,“deviceId”:“328b7d3acf6b9l”,“robot”:{“robotFileId”:“766b3985f71c4902954cefb65d.png”,“robotModel”:“790T”,“robotName”:“proscenic robotic 790T”,“createTime”:“1517281915”,“robotType”:“1”,“control”:{“fanIsPause”:“0”,“directionIsPause”:“0”,“workIsPause”:“0”},“robotId”:“3d3c0bb39e41569379fcebc11026”,“modules”:{“clearModel_9”:“0”,“voice”:“0”,“clearModel_7”:“0”,“clearModel_8”:“0”,“clearModel_5”:“0”,“suportViewState”:“1”,“clearModel_6”:“1”,“clearModel_3”:“1”,“clearModel_4”:“1”,“suportCtrl”:“1”,“suportOrder”:“1”,“clearModel”:[{“clearName”:“Auto”,“clearSign”:“clearModel_3”},{“clearName”:“Area”,“clearSign”:“clearModel_6”},{“clearName”:“Border”,“clearSign”:“clearModel_4”}],“network”:{“softAp”:“SA_NORMAL”,“smartLink”:“SL_NULL”,“noiseLink”:“0”,“softApWiFiSSID”:“Proscenic_”},“vision”:“0”,“radar”:“0”,“fan”:“0”,“attraction”:“0”,“clearModel_1”:“0”,“clearModel_2”:“0”,“clearModel_11”:“0”,“clearModel_10”:“0”,“camera”:“CA_NULL”,“map”:“100”,“waterTank”:“0”},“robotImg”:“http://baole-release.robotbona.com/766b3985f71c4902957a89bc4cefb65d.png",“soft”:{“isNeedUpdate”:“0”}},“firmwareVer”:"{“esp32”:“1.0.41”}",“updateMode”:"",“updatePro”:"",“cameraUid”:“32807d3acf4b94”,“workState”:“6”,“devicePort”:"",“updateModule”:""},“version”:"1.0.0”}

post request with these headers

Accept: application/json
Accept-Encoding: gzip
Accept-Language: en
Content-Type: application/x-www-form-urlencoded
Cookie: JSESSIONID=D162F2B3A22163BC6A5F4EEAC
IsLogin: 1
Origin: chrome-extension://aejoelaoggembcahagimdiliamlcdmfm
User-Agent: blapp

to this address:
http://bl-app-eu.robotbona.com/baole-web/robot/getRobotInfo.do

with this body:
deviceId=07d3acf&nonce_str=15537209&sign=E06D788E33E72266

PS: deviceid, nonce_str, sign and sessionid are changed and are not real - use yours

PS: Im using free sniffer - “Wireshark” from this site
https://www.wireshark.org/#download

Hmmm… Wooo… This is clearly not what I have… I used NetCapture on my android Device, and here what I have:

When starting the robot from the app:
Request send by the “Proscenic server”:

{"cmd":0,"control":{"authCode":"xxxx","targetId":"yyyy","targetType":"1"},"seq":0,"value":{"appKey":"123456","deviceId":"azerty","deviceType":"3","token":"qsdfgh","userId":"poiuytr"}}
The robot repplies:
{"msg":"","result":0,"time":"2019-02-07-04-49-58","version":""}

And then a second information is sent from the “Proscenic server”:
{"cmd":0,"control":{"authCode":"xxxx","deviceIp":"192.168.1.x","devicePort":"8888","targetId":"yyyy","targetType":"1"},"seq":0,"value":{"transitCmd":"100"}}

And the robot answers with all the statuses (battery info etc…)

But on my side, all those queries are sent through UDP (20008 for remote, 8888 on local)

EDIT: I’ll try the direct post to http://bl-app-eu.robotbona.com/baole-web/robot/getRobotInfo.do. Just need to find out how :stuck_out_tongue:

I have the same … when pressing start and pause buttons in the app.
But I can only sniff communication between the app and the server, because I’m not at home right now :slight_smile:

For the direct post I used old chrome plugin called “DHC” … Not sure if it is still available (I mean the plugin).

https://client.restlet.com/ might do the job. I meant I need to find my JSESSIONID, my nonce_str and my sign. I’ll check if I have it on my wiresharks data
Edit: Nop… :frowning: only TCP SSDP and ARP protocols in Wiresharks

Only on app startup is made this http request from the app to the server

Thanks! I always started with th app already opened!
But the cookie is expiring in 2 days… So you need to first send the http request with the app key to generate the JsessionID every 2 days.
I’ll give a try with my data

EDIT: Seems to be on good track, but the POST is rejected:
{ "result": "10001", "msg": "L'utilisateur n'est pas connecté", "errorDetail": "isBreak=false:com.youren.core.RequiredLoginException:L'utilisateur n'est pas connecté\ncom.youren.core.servlet.support.AppSecurityHandlerInterceptor.preHandle()Line:83\norg.springframework.web.servlet.HandlerExecutionChain.applyPreHandle()Line:133\norg.springframework.web.servlet.DispatcherServlet.doDispatch()Line:962\norg.springframework.web.servlet.DispatcherServlet.doService()Line:901\norg.springframework.web.servlet.FrameworkServlet.processRequest()Line:970\n" }

==> Seems to be normal, my robot appears offline :slight_smile:
EDIT: The robot is connected, but I am still having the same message from the post command… I will try to end to complete http post request to the server and see what it says…
What is strange, my app does not ask getMyDefaultRobotInfo.do on the same url… My traces are located to /index/getMyDefaultRobotInfo.do whereas yours is at /robot/getMyDefaultRobotInfo.do… But the issue is still…

OK so starting from the first step, I am getting the JSESSIONID correctly.
`POST /baole-web/common/initSoft.do HTTP/1.1
Accept-Language: fr
Accept: application/json
Cookie:
User-Agent: blapp
IsLogin: 0
Content-Type: application/x-www-form-urlencoded
Content-Length: 130
Host: bl-app-eu.robotbona.com
Connection: Keep-Alive
Accept-Encoding: gzip

nonce_str=1550127&appKey=67ce4fabe5624&channel=TxYyb&deviceId=161390b2&deviceType=3&version=1.5.2`

But then, the login fails, using the hereunder post data:
`POST /baole-web/common/login.do HTTP/1.1
Accept-Language: fr
Accept: application/json
Cookie: JSESSIONID=1F4F5C9D8D5C1E74FA80004AB585C519
User-Agent: blapp
IsLogin: 0
Content-Type: application/x-www-form-urlencoded
Content-Length: 179
Host: bl-app-eu.robotbona.com
Connection: Keep-Alive
Accept-Encoding: gzip

account=f+QN1zDoLvjWNKSW&encryptType=1&isReturnNew=1&loginType=2&nonce_str=155012&pwd=e9HuZNQRbyILkmSrA1/oUw==&sign=EEC3FD5112B10C3`

I realized that the sign value changes everytime (such as the nonce_str, that is incremented at each http request). And I am not able to understand how this value is generated (as it is not provided by the server). I guess there is some kind of computation performed with the JSESSIONID to build the sign value…

‘IsLogin: 0’ should be ‘IsLogin:1’ when loging …

Hello,

I will try again tonight, I think I made a mistake in the copy/paste on the previous message (in the header information).
Edit: Here is the complete test:

First: get a new JSESSIONID:

POST /baole-web/common/initSoft.do HTTP/1.1

Accept-Language: fr

Accept: application/json

Cookie:

User-Agent: blapp

IsLogin: 0

Content-Type: application/x-www-form-urlencoded

Content-Length: 130

Host: bl-app-eu.robotbona.com

Connection: Keep-Alive

Accept-Encoding: gzip

nonce_str=15501277&appKey=67ce4fabe562405d9&channel=TxYyb&deviceId=161390b2d&deviceType=3&version=1.5.2

Then, login:

POST /baole-web/common/login.do HTTP/1.1

Accept-Language: fr

Accept: application/json

Cookie: JSESSIONID=1F4F5C9D8D5C1E74FA (previously obtained)

User-Agent: blapp

IsLogin: 1

Content-Type: application/x-www-form-urlencoded

Content-Length: 179

Host: bl-app-eu.robotbona.com

Connection: Keep-Alive

Accept-Encoding: gzip

account=f+QN1zDoLvjWNKSWc8&encryptType=1&isReturnNew=1&loginType=2&nonce_str=15501&pwd=e9HuZNQRby&sign=EEC3FD5112B10C3DFBDACD8

The result is:

{

“result”: “-1”,

“msg”: “验签失败”,

“errorDetail”: “isBreak=false:com.youren.core.AppException:验签失败\ncom.youren.core.helper.EncryptHelper.validSign()Line:232\ncom.youren.core.helper.ServletHelper.getRequestParameterSecurity()Line:265\ncom.youren.core.auth.controller.AppMainController.login()Line:443\nsun.reflect.GeneratedMethodAccessor341.invoke()Line:-1\nsun.reflect.DelegatingMethodAccessorImpl.invoke()Line:43\n”

}

That’s why I guess the sign value is generated by the app according the SessionID (and why I am having an error when trying to connect with an “old” sign value

1 Like

Hello, i have the same robot 790t did you have completely allready working on Hass. Can you please describe the steps to install it on Hass.

@Himdola Currently it is now working … Someday I’ll have to start over and implement it again …