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 478 forks source link

Add support for "Sweep Frequency" RF #87

Closed lprhodes closed 5 years ago

lprhodes commented 7 years ago

Hi @mjg59

I have the full details on what happens when you press "Sweep Frequency" now.

These payloads are all sent with 0x6a commands.

Start Sweep Frequency: 0x19

Check for found frequency part 1 (repeat once per second): 0x1a Wait for 0x01 as position 0x04 response: 0x1a000000010000000000000000000000

Check for found frequency part 2 (repeat once every second): 0x1b Wait for 0x01 as position 0x04 response: 0x1b000000010000000000000000000000

Use the standard check data (release once per second): 0x04 Wait for hex code response as you would on the normal check data

mjg59 commented 7 years ago

Just to make sure I understand, the flow is:

Send 0x6a, 0x19 Send 0x6a, 0x1a until you get an 0x01 Send 0x6a, 0x1b until you get an 0x01 Send 0x6a, 0x04 to get the response

Is there any need to call 0x6a, 0x03 (the enter_learning command that's used for IR)? Does the orange LED turn on at any point in this process?

ereg commented 7 years ago

Hello @mjg59 ,

I can confirm that the orange LED turns on, directly after entering learning "Sweep Frequency" (0x19, I guess). And it stays on during both steps.

First step is Learning frequency (0x1a ?), a long press on button until it´s done. Second step is Learning Code (0x1b ?). and is only a short press, like with IR code.

Maybe not much help, but love to get this working.

lprhodes commented 7 years ago

I ended up adding it to the JS version - it should help you work things out https://github.com/lprhodes/broadlinkjs-rm/blob/master/index.js

mjg59 commented 7 years ago

@lprhodes Afraid not - it's still not clear what the correct sequence of events is. Is my understanding in https://github.com/mjg59/python-broadlink/issues/87#issuecomment-299725918 correct?

ereg commented 7 years ago

is it possible to try this?

mjg59 commented 6 years ago

Could someone test https://github.com/mjg59/python-broadlink/tree/rf_experiment ? I don't have any RF equipment to test it with.

iondulgheru commented 6 years ago

@mjg59, I made a test and it works on python3 (3.6.3). But on python 2.7 I get a timeout when I run device.check_data(). After that all the calls receive a timeout and I need to reconnect to the device. Otherwise, on python3 works as expected. I also used the returned code (device.send_data(ir_packet)) and I could control the end device.

I used this cheap RF remote to test it: https://www.amazon.com/Lerway-Controller-Wireless-Control-R106/dp/B00AHU2U7O

masarliev commented 6 years ago

@mjg59 Tested with some RF remotes and I get None on devices[0].check_data() Here is example code that I use. May be I missed something

import broadlink
broadlink.setup('ssid', 'password', 3)

devices = broadlink.discover(timeout=5)
devices[0].auth()
devices[0].enter_learning()
devices[0].sweep_frequency()
print("learning")
while 1:
    found = devices[0].check_frequency()
    if found:
        print('Found')
        break

while 1:
    found = devices[0].find_rf_packet()
    if found:
        print(devices[0].check_data())
iondulgheru commented 6 years ago

@masarliev , try increasing your timeout to 120. I got this timeout error also on the master branch, but after I increased it, the device was found. If the device was already setup, and you know the IP and mac, you don't need to run discover. Just create a device object by using: dev = broadlink.gendevice(DEVICE_TYPE_HERE, ("DEVICE_IP_HERE",80), mac=bytearray.fromhex("DEVICE_MAC_HERE")) You can get your device type from here: https://github.com/mjg59/python-broadlink/blob/master/broadlink/__init__.py#L16 It's the devtype hex.

masarliev commented 6 years ago

I don't have problem on device discovery. I don't receive the RF code

laviua commented 6 years ago

Something goes wrong... i've created a loop with "sweep freq (0x19)", sleep, "check freq (0x1a)" until 0x04 == 1 then find rf packet (0x1b) returns 0x04 == 1, but get learned data (0x22, 0x23) returns error...

btw in some cases i can receive rf codes, even if check_frequency is false. i didn't use sweep, find_rf_packet packets.....only learn

a-lurker commented 6 years ago

@brentavery Get rf scan learning working in CLI tool (#87) : I had this RF code learn method working in a Vera plugin I wrote, with an RM Pro (type 0x2787) and as of about early Feb 2018, it stopped working. I now always get (@laviua) learned data (0x22, 0x23) returns an error: where the error is always 0xfff6. Note then when doing an IR learn, the BroadLink device also returns the 0xfff6 error on get data, until it has an IR code to return.

Vera plugin: https://github.com/a-lurker/Vera-Plugin-BroadLink-Mk2/blob/master/Luup_device/L_BroadLink_Mk2_1.lua

So do BroadLink do OTA updates automatically or do you have to manually update its firmware (note I don't use e_Control)? Maybe the firmware has changed? Is there a command to retrieve the firmware version information?

It's worthwhile running the RF code learn method without clicking on any RF remote control button. In my case I get the command: "check freq (0x1a)"; 0x04 == 0x04 when it gives up looking for a RF remote command. That is, when the LED goes out. So I interpret this as "Given up on getting an RF code"

Also using the RF code learn method without clicking on any RF remote control button, I sometimes get spurious: check freq (0x1a)"; 0x04 == 0x01 indicating an RF frequency was found. It's perhaps possible the BroadLink device is picking up misc signals from around the local environment?

It also seems strange that the protocol requires a stop RF learn method when the actual device either times out (LED goes out) with no code found or stops immediately it determines a code (LED goes out). Regardless that's maybe just how its programmed.

Jeroen-R commented 6 years ago

I can confirm that the test code from @masarliev works perfectly to pick up the RF remote for my electric gate (which requires sweep mode). Naturally you need to use the code on the https://github.com/mjg59/python-broadlink/tree/rf_experiment branch.

Hooked it up to the Google assistant (My code is all Python 3), I can now open the gate using voice control when half a mile away ('Open Sesame!'). By the time I drive up it is fully opened.

Saves me from reaching out to the RF remote and wait till I am in range. Saves me a good 10 - 15 seconds a day :smiley:

Is this change expected to be merged and packaged up any time soon?

okaapi commented 6 years ago

The code from @masarliev didn't work for me.... but I got it to work with my AC114-01B RF transmitter for my projection screen, as follows:

(I have to say that this is the first time I've used Python, so maybe its an issue of code versions, and I also can't figure out strings and bytearrays yet :-( ).

Two points:

1 - in check_frequency there is a check

if payload[0x04] == 1:
    return True

which never triggers. I replaced it with

bb = bytearray(payload)
if bb[4] == 1 :
    return True

and that works for me.

2 - calling enter_learning() before sweep_frequency() screws things up for me (as in reboot required), and find_rf_packet() should only be called once! find_rf_packet() seems to be the equivalent of enter_learning() for IR.

So it looks like this:

rm_sweep_frequency() # LED will be red
check_frequency()  # call until true (see #1), 1 sec intervals, requires 5-10 RF button clicks
                   # frequency is now found, and LED turns off
find_rf_packet()   # call only once, LED will turn on (just like with IR enter_learning())
check_data()       # call until result is not empty, usually requires only 1-2 RF button clicks
                   # LED will turn off, and data can be used for send_data()

I can share the code if somebody would like it...

My request: I have this on my Pi, and now I'd like to trigger it with Alexa...

experiment.txt

mjg59 commented 6 years ago

Thanks, that looks like enough detail for me to implement this.

a-lurker commented 6 years ago

okaapi - this looks good but I'm wondering how does one detect when no frequency is found or no button is pushed after a frequency is found. And what to do at those points.

okaapi commented 6 years ago

@a-lurker -

1 - when no frequency is found:

You are in a loop with check_frequency(), and you can obviously break after so many seconds. Also, the infamous \x04 position (which goes to 1 when a frequency is found) is set to 4 after 20 seconds or so, that may be a timeout. I changed my check_frequency() to return \x04 - 0, 1, or 4.

2 - if no button is pushed after the frequency is found: At this point you're in a loop with check_data(), and you can break after, say, 10 seconds.

In both cases, the LED is still red and to get the device out of that state, cancel_learning() to the rescue!!!

  def cancel_learning(self):
    packet = bytearray(16)
    packet[0] = 0x1e;
    self.send_packet(0x6a, packet)

(I found the 0x1e command in this code https://github.com/lprhodes/broadlinkjs-rm/blob/master/index.js thank you @lprhodes )

Here's my code... rf_experiment.txt broadlink.txt

okaapi commented 6 years ago

Regarding Alexa. I have the RM send_data() in a web server on the Pi which responds to simple GET commands to read the codes and send them to the rm. In parallel I have fauxmo talking to Alexa, and sending Get commands to the webserver (https://github.com/makermusings/fauxmo). It all works great for now!

sprilukin commented 5 years ago

I was able to successfully read RF code using provided branch rf-experiment

using python 3 (did not testest on python 2) with the following code snippet (edited version of @okaapi rf_experiment.txt):

import broadlink
import time

device = broadlink.rm(host=("192.168.1.100", 80), mac=bytearray.fromhex("3333333333"))
device.auth()
device.sweep_frequency()
print("sweeping...; LED should be RED")

i = 0
while True:
    f = device.check_frequency()
    if not (f == 0):
        break
    i = i + 1
    time.sleep(1)
    print("check frequency... ", i)

if f == 1:
    print("frequency found! check LED, should be off")
else:
    device.cancel_learning()
    print("frequency not found... ")
    exit()
time.sleep(1)

print("learning command")

device.find_rf_packet()

i = 0
while True:
    data = device.check_data()
    if data:
        break
    elif i > 10:
        device.cancel_learning()
        print("command not learned")
        exit()
    i = i + 1
    time.sleep(1)
    print("check command... ", i)

print("command learned")

encodedData = ''.join(format(x, '02x') for x in bytearray(data))
print(encodedData)

fname = "learn_code.txt"
text_file = open(fname, "w")
text_file.write(encodedData)
text_file.close()

execute RF code which has been written to learn_code.txt:

#!/usr/bin/python
import broadlink

device = broadlink.rm(host=("192.168.1.100", 80), mac=bytearray.fromhex("3333333333"))
device.auth()

file = open("./learn_code.txt", 'r')
myhex = file.read()
print(myhex)
print(bytearray.fromhex(myhex))
device.send_data(bytearray.fromhex(myhex))
sprilukin commented 5 years ago

check out this PR: 218 this is almost the same as 148 but for the latest version of the library at the moment: v0.9, also fixed couple issues in cli tool.

madrose commented 5 years ago

I can share the code if somebody would like it...

My request: I have this on my Pi, and now I'd like to trigger it with Alexa...

[experiment.txt](https://github.com/mjg59/python-broadlink/files/2186468/experiment.txt)

Hi @okaapi is it possible to share the codes for this remote? I have been struggling for a while now to get working codes. Thanks in advance

okaapi commented 5 years ago

Hey madrose,

I can send it no problem, but you might be better off starting with @sprilukin (just one or two posts up) - I have not checked his stuff yet, but it seems to work, and I'll replace my local version with his also so we're all in sync soon :-)

If that doesn't work - or you insist! - I'll send you my files.

madrose commented 5 years ago

Hey madrose,

I can send it no problem, but you might be better off starting with @sprilukin (just one or two posts up) - I have not checked his stuff yet, but it seems to work, and I'll replace my local version with his also so we're all in sync soon :-)

If that doesn't work - or you insist! - I'll send you my files.

Actually I got @sprilukin version to work, and I got some code from my RM. Thanks, but I am wondering how can I convert those codes to something I can use in Home Assistant. I believe that is base64 code (not sure ??)

okaapi commented 5 years ago

Hi... congrats for getting the RM stuff working. I am not familiar with Home Assistant. I combined the RM code with a some other code that lets me control it via Alexa.

On Tue, Dec 4, 2018 at 1:35 PM madrose notifications@github.com wrote:

Hey madrose,

I can send it no problem, but you might be better off starting with @sprilukin https://github.com/sprilukin (just one or two posts up) - I have not checked his stuff yet, but it seems to work, and I'll replace my local version with his also so we're all in sync soon :-)

If that doesn't work - or you insist! - I'll send you my files.

Actually I got @sprilukin https://github.com/sprilukin version to work, and I got some code from my RM. Thanks, but I am wondering how can I convert those codes to something I can use in Home Assistant. I believe that is base64 code (not sure ??)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/mjg59/python-broadlink/issues/87#issuecomment-444267336, or mute the thread https://github.com/notifications/unsubscribe-auth/ABkSF7ETmRiYkJiFMpW1GWnx6Sy8871oks5u1uqRgaJpZM4NK_Da .

rwa commented 5 years ago

This looks like it has the potential to solve the massive headaches I have been encountering trying to learn RF codes for my RM Pro Plus 3. I am a little unclear on the status of this. These fixes haven't been integrated into the code yet? Is there a low pain way to try out the feature?

sprilukin commented 5 years ago

@rwa, in order to try out you can check out my test branch (steps are for linux and pip):

rwa commented 5 years ago

It worked! Not well, I had to learn the code 6 times and got a different one every single time, but one of them did work.

This really should get into a release asap.

gavindsilver commented 5 years ago

hi @sprilukin ;

I tried the above method and I am receiving this:

Learning... No data received... Traceback (most recent call last): File "./broadlink_cli", line 186, in dev.sweep_frequency() AttributeError: 'rm' object has no attribute 'sweep_frequency'

edit: i am using a rm pro plus 3

sprilukin commented 5 years ago

I'm sorry, not sure how I can help. I just needed that stuff to work, so I digged into the code and was in the context 4 months ago. Now I completely forgot how it works ) I do not have that environment set up to check.

steffenu commented 3 years ago
Traceback (most recent call last):
  File "C:/Users/medy1/PycharmProjects/broadlink/RFFINALSEND.py", line 32, in <module>
    ir_packet = devices[0].check_data()
  File "C:\Users\medy1\PycharmProjects\untitled1\venv\lib\site-packages\broadlink\__init__.py", line 631, in check_data
    check_error(response[0x22:0x24])
  File "C:\Users\medy1\PycharmProjects\untitled1\venv\lib\site-packages\broadlink\exceptions.py", line 97, in check_error
    raise exception(error_code)
broadlink.exceptions.ReadError: Read error

when running the script from @sprilukin

using a broadlink pro plus_300

and the master branch

then switching out check data method to the one from the rf_experiment branch

made things work gg

  def check_data(self):
    packet = bytearray(16)
    packet[0] = 4
    response = self.send_packet(0x6a, packet)
    err = response[0x22] | (response[0x23] << 8)
    if err == 0:
      payload = self.decrypt(bytes(response[0x38:]))
      return payload[0x04:]