Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
13.02.2019, 16:04
(This post was last modified: 13.02.2019, 16:05 by gjniewenhuijse.)
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
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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.
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
(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?
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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: -- 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()
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
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'
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
yes, packages are installed.
But how to find the password (device credentials) or is it possible to generate it?
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
15.07.2019, 12:48
(This post was last modified: 15.07.2019, 13:05 by gjniewenhuijse.)
I think i need to get device information over HTTPS:
https://github.com/CharlesBlonde/libpure...k/dyson.py
Code: 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: * 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
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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 Account assword values as Basic auth. See last example in HTTP docs: http://w3.impa.br/~diego/software/luasocket/http.html
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
15.07.2019, 14:34
(This post was last modified: 15.07.2019, 14:34 by gjniewenhuijse.)
(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 Accountassword 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: 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)
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
Can you send your email/password via PM so I can test locally?
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
(16.07.2019, 07:59)admin Wrote: Can you send your email/password via PM so I can test locally? done
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
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.
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
(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: 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
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
When I tested with your account it returned empty device array []
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
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.
Posts: 449
Threads: 94
Joined: Jun 2015
Reputation:
6
17.07.2019, 14:39
(This post was last modified: 17.07.2019, 14:39 by gjniewenhuijse.)
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: 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"]
Posts: 7764
Threads: 42
Joined: Jun 2015
Reputation:
447
Try this for SHA512 + BASE64 encoding:
Code: require('encdec')
password = '123456'
password = encdec.sha512(password, true) -- binary form
password = encdec.base64enc(password)
Python code that you've posted does password decryption.
|