mjg59 / python-broadlink

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

Support for SmartOne Alarm Kit #18

Open eddyyanto opened 7 years ago

eddyyanto commented 7 years ago

Now that the library supports RM (Universal Remote), SP (Smart Plug), A1 (Environment Sensor) and their OEM branded variants, it would great if it also support Broadlink SmartOne Alarm Kit.

Broadlink SmartOne Alarm Kit has the following kits:

eddyyanto commented 7 years ago

Previously, I had tried to statically set the SmartOne Host as RM device but it doesn't work.


import broadlink
import time

# SmartOne Host device ip address: 192.168.0.174 and mac address: b4:43:0d:95:fd:6a
device = broadlink.rm(host=("192.168.0.174", 80), mac=bytearray([0xb4, 0x43, 0x0d, 0x95, 0xfd, 0x6a]))
device.auth()
device.enter_learning()

time.sleep(5) # press any key on the fob remote within 5 seconds

ir_packet = device.check_data()
print ir_packet # result in None
mjg59 commented 7 years ago

The API supports subdevices, but I don't have any hardware that implements them. I suspect that the additional devices are accessed via commands sent to or from the host device, but I haven't looked at the protocol for that at all. Patches very welcome.

akshitgupta95 commented 7 years ago

support for Broadlink S1 would be awesome.

radinsky commented 7 years ago

I have the hardware, any chance you can guide me or give a hint where to start looking in case to add S1 support? I see there's some hardcoded devtypes ?

uvandres commented 7 years ago

@mjg59 I have 2 s1 with lots of sensors installed in 2 different locations. Can I help someway to make this happen?

radinsky commented 7 years ago

@mjg59 So what do you think, can you give us some clue or hint where to start digging?

uvandres commented 7 years ago

@mjg59 Any updates on this? I'd love to be able to manage the s1 from the python as well. That way I will be able to fully uninstall the broadlink app from my phone :)

pavram commented 7 years ago

In order to implement this; we'd need to do packet captures from the original hardware/remote software to capture each side of the communications and a copy of each of the events.

The AES encryption also means that we would need an authentication response for each packet-capture session to ensure we could decrypt the data. (because the key changes between sessions I think).

None of this is insurmountable, it's just a little bit of a pain to set up, and quite difficult to talk someone else through.

The starting point is wireshark, and an android emulator like Bluestacks.

Wireshark will capture a lot of information, most of it completely useless for this purpose, so you will need to filter the traffic to just the traffic relating to the broadlink device. (Not too hard if you have the device IP address to filter to just that)

Because wireshark doesn't understand the protocol; it doesn't show it nicely. So you will also need to perform your test requests and markup the packet captures with the times that you perform an event (so you know when a set of packets should relate to a specific command).

If you manage to get all of that, and export a wireshark packet capture someone here could probably attempt to implement the new packets, but then things like the lock sensors are difficult to implement without the device in your hands.

radinsky commented 7 years ago

I can do the sniffing and provide the session pcap capture. Will you able to process it?

pavram commented 7 years ago

@radinsky I will have a look.

But just in case I drop off the face of the earth. when you do the packet capture attach it to this thread. That way even if I dont do it; it will be infinitely closer to being done.

Just remember to filter the pcap that you export to just comms to/from the device and App!

I wrote a tool that can inspect packets (assuming you pass in an Auth-reply that includes the key) but its written in c#, and I hate the idea of spreading executables.

johado commented 7 years ago

On my johado fork I also have started on a tool in Python to help decoding pcap dumps, maybe you find it useful. (It uses the auth key from a previous discovery stored in a json file, so it probably need some adjustments to create that based on the dump and not an actual discovery.)

radinsky commented 7 years ago

Please find attached pcap for s1c session. I only filtered the traffic (by ip) to and from the s1c. Everything captured on router itself (without internet connection) on wireless interface. IP's: s1c 192.168.1.239 MBP 192.168.1.226 iPhone 192.168.1.100

I did ping to the s1c from MBP between some actions (just to be a bit easier to recognize where the command finish itself).

My scenario (ping between each of them)

s1c.pcap.zip

Let me know if I can help @pavram

pavram commented 7 years ago

Sorry to be a pain @radinsky, but it turns out the app is slightly more annoying than I realised to get packet captures.

The app (I have verified this) caches the client is and the encryption key between launches.

Which means that since you didn't learn the device as new during the packet capture (I suspect you just powered it on) the actual auth request and auth reply aren't in the capture.

(I was doing my own packet captures and had the same issue).

In the Android app, I had to go to device list, long press the device and delete it, then add it again during the packet capture.

There's no need to reboot the device from scratch, just delete it from the app and add it from scratch,. THEN perform all your actions again.

I was looking at the file system for the app, and I believe it stores all this data in a filesystem based database (in android it is a Java implementation of an LRU cache according to the markup in the log file) which I suspect may contain the keys we need, but it is usually easier to grab another iPhone device and Wireshark while adding it and doing your tests over again.

(Doing the tests from a new iPhone is probably easier than tearing down your existing configuration off your primary device - though it does seem to have some kind of "export" function that might backup your config).

If you do go through the hassle of doing it from your primary device, the good news is future captures should use the same key, obviating the need to capture the auth key for future dumps.

radinsky commented 7 years ago

Hey

Please find new pcap as you required, SmartONE removed from the app, reset and adding the sensors again. This time it required the internet connection, when you're adding new sensor, if no internet connection detected the adding process is failing (probably tries to verify the serial of the sensors with broadlink servers). Filtred the capture by s1c ip.addr, also I saw few packets between s1c and rm2 and a1 devices, filtered also, let me know if you want it as well. IPs: s1c 192.168.1.239 MBP 192.168.1.226 iPhone 192.168.1.100

Updated scenario:

s1c_100517.pcap.zip

Let's see if it's better now @pavram

pavram commented 7 years ago

@radinsky looks good.

Just on a lunch break right now so I haven't teased the rest apart yet, but packet 99 in that capture is an Auth reply from the s1c .239 to the .100 iPhone.

I'll extract the rest of the payloads from this packet capture hopefully tonight, which will give us something to look at and build to.

pavram commented 7 years ago

I've reformatted the packet capture into a simple file (including lines for the PING sections)

s1c_simple.txt

I have also built a web service that uses my library to investigate the format to make it easier for me. I'll use this to process the above data.

http://onlinebroadlinkinspector.azurewebsites.net/Inspect/Report

simple guide for the webservice is if it is encrypted; either include the AES_key specifically for the packets, or include the auth-reply packet first in the packets you paste in.

(you can paste the whole txt file dump in at once, it ignores blank lines and lines beginning with # if you want to add info to your own dumps as you go).

radinsky commented 7 years ago

stuck, tried your online inspector, don't know what to look for..

pavram commented 7 years ago

Its all good. I've been working backwards.

The broadlink protocol uses the "data" and "data_reply" type packets to transmit requests and info.

Due to the encryption the payloads must be a multiple of 16 bytes long (32 characters, or 2 of the segments my inspector outputs).

So when your phone asks for the details of the sensors; it sends out a payload with a single piece of information. Usuaully a "06" as the very first byte in its payload.

The reply to the payload also begins with a 06.

Early on this payload is a whole lot of nothing. (literally its a "06" followed by 1359 bytes of "00").

The last couple of packets (in the 1300's) the output becomes its most interesting, specifically:

0600000002008001 0131446f6f722053 
656e736f72000000 0000000000000000 
310116a5f1faa195 0200000000000000 
0000000000000000 1f000000002d0000 
1e08000000000000 1d08000000000000 
1c0b000000000000 000002012157616c 
6c204d6f74696f6e 2053656e736f7200 
00000021022b89d6 ec4e770200000000 
0000000000000000 0000001f00000000 
2d00001e08000000 0000001d08000000 
0000001c0b00001e 

If you run that through a hex -> Ascii converter, you get to see the names of the devices "Door Sensor" and "Wall Motion Sensor"

So; payload reply with a leading 06 seems to be a status request.

earlier; you "add the wall sensor", the 06 packet before these contains only the door sensor (as expected), after a 07 packet request (the reply to which is a 06 reply, labeled 07 but with the new data)

06 Reply (before)

0600000001008001 0131446f6f722053 
656e736f72000000 0000000000000000 
310116a5f1faa195 0200000000000000 
0000000000000000 1f000000002d0000 
1e08000000000000 1d08000000000000 
1c0b000000000000

07 Request (important; because it shows what settings are specific to a "Wall Sensor" in this case.)

0700000000012157 616c6c204d6f7469 
6f6e2053656e736f 720000000021022b 
89d6ec4e77020000 0000000000000000 
00000000001f0000 00002d00001e0800 
00000000001d0800 00000000001c0b00 
0068010000000000 0000000000000000 

07 Reply (which looks like a 06 reply - now including all devices)

0700000002008001 0131446f6f722053 
656e736f72000000 0000000000000000 
310116a5f1faa195 0200000000000000 
0000000000000000 1f000000002d0000 
1e08000000000000 1d08000000000000 
1c0b000000000000 000002012157616c 
6c204d6f74696f6e 2053656e736f7200 
00000021022b89d6 ec4e770200000000 
0000000000000000 0000001f00000000 
2d00001e08000000 0000001d08000000 
0000001c0b000068 0100000000000000 

Basically, 06 seems to get the "current state" of all devices. 07 appears to add devices. I think a 10 or 12 changes global settings (like "Alarm" modes)

Identifying the important packets is first; then identifying how they encode the info we are interested in can happen.

jazzina commented 7 years ago

Hi. Ive just implemented s1 sensors status checking. Its dirty, but its works. https://github.com/jazzina/python-broadlink/

radinsky commented 7 years ago

Looking good so far @jazzina , would you prepare a pull request to merge it to this repo?

jazzina commented 7 years ago

yeap, done https://github.com/mjg59/python-broadlink/pull/103

nitaybz commented 7 years ago

Hey Guys, great great work @jazzina for the S1C implementation, I already made a javascript library & plugin out of it so I can use with homebridge. Everything works great so first of all thank you! I've been waiting for this for a while...

I wanted to know if there's gonna be some more progress? setting different alarm states? reading "Triggered" state?

radinsky commented 7 years ago

@nitaybz for triggering (as unitest as well) I'm using polling and checking if the status of a device was changed. something like this:

devices = discover(timeout=1)
print (devices)
for d in devices:
  if str(d.type) == 'S1C':
    s = d
s.auth()

sens = s.get_sensors_status()
old = sens
while 1:

  for i, se in enumerate(sens['sensors']):
    if se['status'] != old['sensors'][i]['status']:
     print (time.ctime(), 'Name:', se['name'], 'Status:', se['status'], 'Type:', se['type'])
     old = sens
nitaybz commented 7 years ago

@radinsky this will trigger any time the sensors changes, what I'm referring to is to get the "triggered" state that happens only when the alarm is armed. that way I can use the sensor all day long and be notify when the alarm is armed and one of the sensors catch movement/contact

pavram commented 7 years ago

Nice work all, I've done some more poking around and have extracted each of the packets and a "state" before and a "State" after for each event.

We might need more packet dumps to isolate specific signals but I have found the following:

The aggregate state of the whole system is determined by performing 3 seperate requests.

The following are "doing" packets. Change state or settings or something else.

I have included the packet ranges (but not the actual packets) in my txt files below, so you can decrypt the packets you are interested in if you think I haven't included something important.

The following is a list of packets and states before and after changes. With a little bit of analysis by me.

1 - AddDoorSensor.txt 2 - Door open Door Close.txt 3 - Arm Alarm.txt 4 - Test Alarm with DoorSensor.txt 5 - Disarm Alarm.txt 6 - Add Motion Sensor.txt 7 - Motion Detect Person.txt 8 - Configure Motion time delay.txt

edit: I just googled the s1c, and in the documentation it says it can manage "upto 16 devices". My diagnosis in file 6 above is that packet-type 10 supports a maximum of 16 devices. It is always good when speculation is accurate!

radinsky commented 7 years ago

@pavram thanks! just to be clear regarding the "blank dot points" - it's actually I did nothing, was busy by writing down the test steps) so it was like a pause for several moments, you can see it as well in capture file (packet times).

Anyway, would you like me to make some additional capturing/sniffing? If so, could you please specify the scenarios to sniff?

radinsky commented 7 years ago

Hey again guys,

So the s1c works perfect on my branch, I did integrate it to HomeAssistant as well.

What I'm curious now, how to allow non broadlink sensors (pir/door/windows sensors and maybe others?) to be learned by s1c?

NightRang3r commented 7 years ago

How did you integrate it ? Do you experience timeouts ?

PeggyFree commented 7 years ago

Hello,

Thank you for this work. I'm totally noob in python. I tried @jazzina 's source code together with the @radinsky 's sample code proposed above. Using a polling method, I can get these devices events : remote buttons pressed, sensor events. Here are my questions :

pavram commented 7 years ago

Hi @PeggyFree, of all the packet captures I investigated, there didn't appear to be any packets that originated from the S1C itself (without an associated request asking for the data). Which means Polling is the only way I am aware of to check sensor states.

I know it seems kind of wrong to be spewing packets every 1-5 seconds, but that's pretty much the only way I am aware of, and it appears to be the way that the official broadlink app does it.

There is a chance that the broadlink device itself initiates a communication to send alerts back to Broadlink HQ, it is possible that we could try to intercept this, but I doubt we could modify it easily.

(I suspect we could try to intercept the DNS requests for the device during boot and hijack that communication between device and server... No one has attempted this yet as far as I am aware).

nick2525 commented 4 years ago

Why it is not supported if we have such pyhton script https://github.com/TomerFi/home-assistant-custom-components/blob/master/broadlink_s1c/custom_components/broadlink_s1c/sensor.py

felipediel commented 3 years ago

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

KTibow commented 3 years ago

Wait. What? That was closed, not merged.

felipediel commented 3 years ago

That was the original PR. mgj merged with this commit. S1C is supported now.

nick2525 commented 3 years ago

@felipediel but is not supported in HA yet?

felipediel commented 3 years ago

No. HA is a different code base, this is the API. I will reopen the issue because the implementation is incomplete, maybe someone wants to improve it.

nick2525 commented 2 years ago

Thank you.