mjg59 / python-broadlink

Python module for controlling Broadlink RM2/3 (Pro) remote controls, A1 sensor platforms and SP2/3 smartplugs
MIT License
1.39k stars 479 forks source link

New device, SP4L-EU #299

Closed skrutt closed 3 years ago

skrutt commented 4 years ago

Hey all

So, i just started in this and bought a clas ohlsson "smart plug" wifi-switch to have something to play with hass.io.

I found that there seemed to be support, but later found out that i of course have seem to found a new version of the plug? Last posts i found about this plug was from january, so i guess they have upgraded since then.

I have followed the readme (and tried most of the device types supported in hass.io on Rpi), i got this:

devices[0].auth()
True
hex(devices[0].devtype)
'0x7579'
devices[0].get_type()
'Unknown'

I also tried the setup(), that did really not seem promising(i had added it to network with the clas-app first), but after a bunch of tries, resets and a rename of network it seemed to work and got added to network again.

Soo, it would be cool if i i could add this one myself, having some programming experience, but not so much experience in python/network stuff.. so i figure i might stand a better chance if i give a shoutout here.

Here is some packet data from wireshark, UDP broadcast: 0000 5a a5 aa 55 5a a5 aa 55 01 00 00 00 e3 07 32 2a 0010 14 05 0f 0b 00 00 00 00 56 01 a8 c0 38 99 00 00 0020 bb c6 00 00 00 00 06 00 00 00 00 00 00 00 00 00

So i guess i'll have a go at adding a new type or maybe just a new identifier now, wish me luck.

skrutt commented 4 years ago

Comparing the data captured from the app and the below data sent from this lib(if it would be an sp2 device), some things to note:

Looking at the responses:

Other than this, there are some messages sent to three what appears to be aws servers.. is this the path for the actual on/off commands? There seems to be very little difference in performance if phone is connected to wifi or 4g, is it always taking that route? I have not been able to sniff any responses from the cloud, since neither computer or phone is in the path from cloud to switch.. could probably be done but not my forte as stated, i guess a tap or better wifi sniffing could be used. Other explanation is that the on/off is encoded in the hello message from the app, could be possible but so far i have not been able to infer any direct pattern between the packets that would indicate this, the only changes in data seem to be checksums, counter bytes and source port.

0000 5a a5 aa 55 5a a5 aa 55 00 00 00 00 00 00 00 00 0010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0020 29 fd 00 00 2a 27 65 00 fc e6 4b b0 2d a7 df 24 0030 01 00 00 00 a1 c3 00 00 4f b7 c3 80 8c ce 29 98 0040 ec 93 9b 70 dc b8 ae cf 40 2c d7 39 7e 87 95 95 0050 1b ce 3a 6b bd 0b 67 50 ba 08 dc 2e 08 26 ef 3c 0060 b4 97 c4 be c7 8a b0 ac ed a7 fc 61 86 f8 ce 28 0070 98 97 d2 81 c5 93 9c cc 82 9b b7 fd 28 26 57 15 0080 89 5d 72 fe 82 82 10 61 58 23 b0 0e e6 ff 3e c3 0090 c4 5f 5c 7c ae e7 56 57

skrutt commented 4 years ago

I guess, next thing will be to check if it works without an internet connection, and if i see any different packages then

skrutt commented 4 years ago

Does not seem to work without internet connection, i guess i should either find a way to listen directly in line between internet connection and plug, or possibly try out some other app and see if they have any different means of communication!

skrutt commented 4 years ago

getting somewhere!

teststr2 = "5386f245b31c92620ece95b1e32b0a166ae74f42238621f6d78989de0aebf175" teststr3 = "1ba85eb8e2a87a422b87c79cba56d91b5afaf53f47fdf1f785e8eb5605ccaf1b" dev.decrypt(b''.fromhex(teststr2)).hex() 'a5a55a5a68c5020b0d0000007b226e746c69676874223a317d00000000000000' dev.decrypt(b''.fromhex(teststr3)).hex() 'a5a55a5a67c5020b0d0000007b226e746c69676874223a307d00000000000000' dev.decrypt(b''.fromhex(teststr3)) b'\xa5\xa5ZZg\xc5\x02\x0b\r\x00\x00\x00{"ntlight":0}\x00\x00\x00\x00\x00\x00\x00' dev.decrypt(b''.fromhex(teststr2)) b'\xa5\xa5ZZh\xc5\x02\x0b\r\x00\x00\x00{"ntlight":1}\x00\x00\x00\x00\x00\x00\x00'

Got a different app, IHC, using packet capture to read out some data from that after pairing it. This seems to be offline and also sends a package directly to the plug over the wifi.

I think that i can hack it to switch from this, but not quite sure how to interpret the extra data.. a5a55a5a seems to just be a identifier, but the rest?

teststr = "b58e49125f67df59d2e7f605c7e6c8b2" dev.decrypt(b''.fromhex(teststr)) b'\xa5\xa5ZZ\xb3\xc1\x01\x0b\x02\x00\x00\x00{}\x00\x00' dev.decrypt(b''.fromhex(teststr)).hex() 'a5a55a5ab3c1010b020000007b7d0000'

Now this one looks like a status update, my guess is identifier four bytes, checksum 2 bytes, 1 byte read/write flag, and then.. another checksum? then we have the curlybraces so that seems like a verification that im on the right track.

skrutt commented 4 years ago

probably something like the bg1!

packet format is:

    # 0x00-0x01 length
    # 0x02-0x05 header
    # 0x06-0x07 00
    # 0x08 flag (1 for read or 2 write?)
    # 0x09 unknown (0xb)
    # 0x0a-0x0d length of json
    # 0x0e- json data
    packet = bytearray(14)
    length = 4 + 2 + 2 + 4 + len(js)
    struct.pack_into('<HHHHBBI', packet, 0, length, 0xa5a5, 0x5a5a, 0x0000, flag, 0x0b, len(js))
skrutt commented 4 years ago

packet = b''.fromhex('a5a55a5a0000010b020000007b7d0000') checksum = 0xbeaf for i in range(len(packet)): checksum += packet[i] checksum = checksum & 0xffff hex(checksum) '0xc1b3'

Oh yea.

So we compute the checksum without any checksum set in the string... tried that first ;) but obviously we cant have a checksum in there before we calculate it.

But i think i have cracked it now: identifier, 4 bytes checksum 2 bytes (from 0xbeaf) flag read/write, 1 byte, 0x1 or 0x2 0xb, this could be start of json -1? does not quite add up, but if it does not change, does it matter? length of json, 4 bytes json data

Next up, verify if this works! Also do some more querys to find out all identifiers, 'ntlight' is for the night light, there are obviously more! Switch state is one ofc, but i also saw that the nightlight is dimmable in the IHC app, this is not present in clas ohlssons (swedish)app.

This kind of makes for a decent blog of my first experience in reverse engineering/hacking a wifi device

Extra verification:

packet = b'\xa5\xa5ZZ\x00\x00\x02\x0b\r\x00\x00\x00{"ntlight":0}\x00\x00\x00\x00\x00\x00\x00' checksum = 0xbeaf for i in range(len(packet)): checksum += packet[i] checksum = checksum & 0xffff

hex(checksum) '0xc567'

YES! i spent like 20 hours on this over the weekend so im pretty happy right now!

skrutt commented 4 years ago

bragging

dev.type 'SP4' dev.auth() True dev.get_state() {'pwr': 1, 'ntlight': 0, 'indicator': 1, 'usbpwr': 0, 'maxworktime': 0, 'usbmaxworktime': 0, 'ntlbrightness': 59, 'current': -1, 'volt': -1, 'power': -1, 'totalconsum': -1, 'overload': -1}

There is no usb on this device, so i guess that it's sharing firmware with some other device.. also the energy metering is probably not present so therefore -1. Would be nice if you could enable it..

nihilus commented 4 years ago

@skrutt Any updates on this? It would be nice to have some working code.

skrutt commented 4 years ago

@nihilus Hi! Nice to hear that someone is interested.

Yeah, i have tried and verified all functions and i think it should work for others as well. The plan was to clean up some and do a full integration with a pull request yesterday, but i did not quite have time.. should be done tonight 👍

Verified functions include:

nihilus commented 4 years ago

@skrutt I am looking forward for the PR.

skrutt commented 4 years ago

Pull request added at https://github.com/mjg59/python-broadlink/pull/300

daniel-soli commented 4 years ago

Hi Skrutt, I have also got the new clas ohlson wifi plug (SP4), and got dissapointed when I couldn't get it to work in home assistant. I'm not a python wiz, so can you give me a description on how you got this to work? I understand I must somehow update my broadlink library, but have no idea on how to do so. Thanks in advance :)

skrutt commented 4 years ago

Hi @danielsolistensvik! I have been putting off publishing my custom component for this, just because it's more in a dev-state right now and could use some polishing, user-friendliness-wise.

But since you asked and im sure more will follow, i'll post it under my account shortly.

UPDATE: https://github.com/skrutt/broadlink_sp4_customcomponent

daniel-soli commented 4 years ago

Thanks alot @skrutt ! It works like a charm :) . I understand that it may come with some extra logging etc, I saw that in the code. Could just comment out _LOGGER... ?

Is it also possible to copy the structure from broadlink directly and add your class for sp4, change the name from broadlink to something else (or keep it to overwrite broadlink), so that you do not add the devices directly, but instead add them as normal in configuration?

skrutt commented 4 years ago

Nice! But please continue any more discussion on my repo for the integration, this git is for the broadlink library.

Any requests or feedback are welcome, if you have any issues or ideas give feedback on my github

felipediel commented 3 years ago

Fixed with https://github.com/mjg59/python-broadlink/pull/429. Thank you!