This forum uses cookies
This forum makes use of cookies to store your login information if you are registered, and your last visit if you are not. Cookies are small text documents stored on your computer; the cookies set by this forum can only be used on this website and pose no security risk. Cookies on this forum also track the specific topics you have read and when you last read them. Please confirm that you accept these cookies being set.

Dyson Pure Hot+Cool
#1
Has anyone connected a Dyson Pure Hot+Cool 2018 (244289-01) or maybe another Dyson product that used the same protocol?


https://shop.dyson.nl/dyson-ventilatoren...-244289-01
Reply
#2
There are some Python libraries for control: https://github.com/CharlesBlonde/libpurecoollink

Internally MQTT is used which is supported by LM, but you need to get credentials before MQTT connection is possible.
Reply
#3
(15.02.2019, 08:34)admin Wrote: There are some Python libraries for control: https://github.com/CharlesBlonde/libpurecoollink

Internally MQTT is used which is supported by LM, but you need to get credentials before MQTT connection is possible.

I already installed this device on my local network and have the right credentials for the Dyson app. How to start?
Reply
#4
Use standard MQTT example without encryption. Username is device serial number, password value are device credentials.

This resident script will connect to MQTT and subscribe to status topic. See logs tab for output result.

Code:
1234567891011121314151617181920212223242526272829303132
-- product types DYSON_PURE_COOL_LINK_TOUR = '475' DYSON_PURE_COOL_LINK_DESK = '469' DYSON_PURE_HOT_COOL_LINK_TOUR = '455' DYSON_360_EYE = 'N223' broker = '192.168.1.1' -- default port is 1883 username = '...' -- device serial password = '...' -- device credentials producttype = DYSON_PURE_HOT_COOL_LINK_TOUR topic = producttype .. '/' .. username .. '/status' mqtt = require('mosquitto') client = mqtt.new() client.ON_CONNECT = function(status, rc, err)   if status then     log('connect ok')     client:subscribe(topic)   else     log('connect error', rc, err)   end end client.ON_MESSAGE = function(mid, topic, data)   log('message', topic, data) end client:login_set(username, password) client:connect(broker) client:loop_forever()
Reply
#5
where to find the mosquitto module?

Resident script:19: module 'mosquitto' not found:
no field package.preload['mosquitto']
no file './mosquitto'
no file 'Library mosquitto'
no file 'Library mosquitto'
no file 'Library mosquitto.so'
stack traceback:
[C]: in function 'require'
Reply
#6
Install these packages:

https://dl.openrb.com/pkg/libmosquitto_1.6.3-1_imx6.ipk
https://dl.openrb.com/pkg/luamosquitto_0.3-2_imx6.ipk
Reply
#7
(12.07.2019, 09:47)admin Wrote: Install these packages:

https://dl.openrb.com/pkg/libmosquitto_1.6.3-1_imx6.ipk
https://dl.openrb.com/pkg/luamosquitto_0.3-2_imx6.ipk

and for a lm4?
Reply
#8
https://dl.openrb.com/pkg/libmosquitto_1.6.3-1_mxs.ipk
https://dl.openrb.com/pkg/luamosquitto_0.3-2_mxs.ipk
Reply
#9
yes, packages are installed.

But how to find the password (device credentials) or is it possible to generate it?
Reply
#10
I think i need to get device information over HTTPS:
https://github.com/CharlesBlonde/libpure...k/dyson.py


Code:
1234567891011121314151617181920212223242526
https = require 'ssl.https' require('json') socket.http.TIMEOUT = 5 local cUsername = 'xxxxxxxxx@xxxxxxxx.nl' local cPassword = 'xxxxxxxxxxxxxx' -- get session info local cBody = '{"Email":"' .. cUsername .. '","Password":"' .. cPassword ..'"}' local cReq = {} local cUrl1 = 'https://api.cp.dyson.com/v1/userregistration/authenticate?country=NL' result1 = https.request({     url = cUrl1,     method = 'POST',     headers = {       ['content-length'] = #cBody,       ['content-type'] = 'application/json'     },     source = ltn12.source.string(cBody),     sink = ltn12.sink.table(cReq) }) if result1 and cReq then   log(cReq)   cAuth = cReq else   log(result1) end


result:

Code:
123
* table: [1]   * string: {"Account":"xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx-xxxxxxx","Password":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}

How to get the account and password value in the following GET request to receive al devices from: /provisioningservice/manifest
Reply
#11
Send GET request to https://api.cp.dyson.com/v1/provisioning...e/manifest after the first one to get device list. You need to decode the first result using JSON and then pass AccountTongueassword values as Basic auth. See last example in HTTP docs: http://w3.impa.br/~diego/software/luasocket/http.html
Reply
#12
(15.07.2019, 13:27)admin Wrote: Send GET request to https://api.cp.dyson.com/v1/provisioning...e/manifest after the first one to get device list. You need to decode the first result using JSON and then pass AccountTongueassword values as Basic auth. See last example in HTTP docs: http://w3.impa.br/~diego/software/luasocket/http.html

i added Account and Password settings to the code below, but i only get a 403 error.


Code:
1234567
mime = require("mime") r, c = https.request {   url = 'https://api.cp.dyson.com/v1/provisioningservice/manifest',   method = 'GET',   headers = { authentication = "Basic " .. mime.b64("xxxxxxxxx:xxxxxxxxxxxx") } } log(r,c)
Reply
#13
Can you send your email/password via PM so I can test locally?
Reply
#14
(16.07.2019, 07:59)admin Wrote: Can you send your email/password via PM so I can test locally?
done
Reply
#15
Thanks, turns out that example is incorrect. Correct header name should be authorization, not authentication. You also need to add sink table to the GET request so you can retrieve response data.
Reply
#16
(16.07.2019, 09:09)admin Wrote: Thanks, turns out that example is incorrect. Correct header name should be authorization, not authentication. You also need to add sink table to the GET request so you can retrieve response data.

Did you receive a response with a device in it?

Code:
12345678910111213
mime = require("mime") local cReq2 = {} local cUrl2 = 'https://api.cp.dyson.com/v1/provisioningservice/manifest' result2, c, h = https.request({   url = cUrl2,   method = 'GET',   headers = {       ['authorization'] = "Basic " .. mime.b64(cAccount..":"..cPassword)   },   sink = ltn12.sink.table(cReq2) }) log(result2,c,h) log(cReq2)

Now my response is a correct http request (code 200), but no data in cReq2
Reply
#17
When I tested with your account it returned empty device array []
Reply
#18
mmm something must be wrong, because i have one device in my account. I can control this device by the app with the same credentials.
Reply
#19
My model is HP04, inside is a code on a label, maybe i can use this because i read:
(After this initialization, you'll have to use the hashed password to connect to the fan. The hashed password is a base64 encoded of the sha512 of the password written on manual and on the fan label)

How to do this encryption in lua, in the Original python code i see:


Code:
123456789101112131415
def decrypt_password(encrypted_password):     """Decrypt password.     :param encrypted_password: Encrypted password     """     key = b'\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10' \           b'\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f '     init_vector = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \                   b'\x00\x00\x00\x00'     cipher = AES.new(key, AES.MODE_CBC, init_vector)     json_password = json.loads(unpad(     cipher.decrypt(base64.b64decode(encrypted_password)).decode('utf-8')))     return json_password["apPasswordHash"]
Reply
#20
Try this for SHA512 + BASE64 encoding:
Code:
12345
require('encdec') password = '123456' password = encdec.sha512(password, true) -- binary form password = encdec.base64enc(password)

Python code that you've posted does password decryption.
Reply


Forum Jump: