Logic Machine Forum
Dyson Pure Hot+Cool - Printable Version

+- Logic Machine Forum (https://forum.logicmachine.net)
+-- Forum: LogicMachine eco-system (https://forum.logicmachine.net/forumdisplay.php?fid=1)
+--- Forum: Gateway (https://forum.logicmachine.net/forumdisplay.php?fid=10)
+--- Thread: Dyson Pure Hot+Cool (/showthread.php?tid=1907)

Pages: 1 2


Dyson Pure Hot+Cool - gjniewenhuijse - 13.02.2019

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-en-verwarmingsventilatoren/luchtreinigers/dyson-pure-hot-cool-zwart-nikkel-244289-01


RE: Dyson Pure Hot+Cool - admin - 15.02.2019

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.


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 11.07.2019

(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?


RE: Dyson Pure Hot+Cool - admin - 11.07.2019

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()



RE: Dyson Pure Hot+Cool - gjniewenhuijse - 12.07.2019

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'


RE: Dyson Pure Hot+Cool - admin - 12.07.2019

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


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 12.07.2019

(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?


RE: Dyson Pure Hot+Cool - admin - 12.07.2019

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


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 15.07.2019

yes, packages are installed.

But how to find the password (device credentials) or is it possible to generate it?


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 15.07.2019

I think i need to get device information over HTTPS:
https://github.com/CharlesBlonde/libpurecoollink/blob/master/libpurecoollink/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


RE: Dyson Pure Hot+Cool - admin - 15.07.2019

Send GET request to https://api.cp.dyson.com/v1/provisioningservice/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


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 15.07.2019

(15.07.2019, 13:27)admin Wrote: Send GET request to https://api.cp.dyson.com/v1/provisioningservice/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:
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)



RE: Dyson Pure Hot+Cool - admin - 16.07.2019

Can you send your email/password via PM so I can test locally?


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 16.07.2019

(16.07.2019, 07:59)admin Wrote: Can you send your email/password via PM so I can test locally?
done


RE: Dyson Pure Hot+Cool - admin - 16.07.2019

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.


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 16.07.2019

(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


RE: Dyson Pure Hot+Cool - admin - 16.07.2019

When I tested with your account it returned empty device array []


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 16.07.2019

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.


RE: Dyson Pure Hot+Cool - gjniewenhuijse - 17.07.2019

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"]



RE: Dyson Pure Hot+Cool - admin - 18.07.2019

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.