radinsky / broadlink-http-rest

Broadlink RM/RM2/RM Pro/RM3/BlackBean/A1 Web server with REST API (like..)
MIT License
156 stars 47 forks source link

New device handling #13

Closed uudruid74 closed 6 years ago

uudruid74 commented 6 years ago

This allows support for multiple devices (with backward compatibility to exiting configurations), support to autodetect devices on the network, support for newer broadlink libraries, and better timeout handling. The last finally solves the "stuck server" problems. The server responds with a 1 second delay, usually almost no delay at all. Global and/or per-device command lists. And some support for multi-homed hosts (one of my IP's is a DMZ and I don't want to control the TV from the internet). Multiple device support is largely untested - I gave multiple names and config sections to the same physical device and it works.

I'm not really a Python programmer, so it's kinda thrown together. It really needs refactoring in many places and who knows what else. But ... it's running pretty well here.

uudruid74 commented 6 years ago

OK - I'm done playing with this for awhile. It should figure things out even with a non-existant Settings.ini, hopefully backwards compatible to all old URLs, etc. Now to see if I can get Google Voice to talk to it!

uudruid74 commented 6 years ago

To address the multi-device config. I can only make multiple devices with the same MAC/IP and different names and this works. I only have the one device to test with

radinsky commented 6 years ago

hey @uudruid74 , i'm failing with the autodetection:

python server.py
Reading device configuration for LivingRoom-BlackBean
Beginning device auto-detection ...
Traceback (most recent call last):
  File "server.py", line 326, in <module>
    devices = broadlink.discover(DiscoverTimeout,listen_address)
  File "/Library/Python/2.7/site-packages/broadlink-0.4-py2.7.egg/broadlink/__init__.py", line 70, in discover
    cs.bind((local_ip_address,0))
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 49] Can't assign requested address
uudruid74 commented 6 years ago

@radinsky Looks like you are using the sample settings.ini. There is a line setting the "serverAddress" to 192.168.12.200. It's purely for multi-homed hosts and exists in the file to show how to set it. You can just delete that line and it will revert to 0.0.0.0, or you can replace it with your local IP. You might want to delete the LivingRoom-Blackbean section as well

radinsky commented 6 years ago

hey, trying you recent commit, updated the settings.ini, but after I start the server it looks like it overwrites settings.ini file with empty:


bash-3.2$ pwd
/tmp/broadlink-http-rest
bash-3.2$ cat settings.ini
[General]
Timeout = 2
serverPort = 8081
serverAddress = 10.0.1.41
Autodetect = 6

[Commands]

[RM2Salon]
IPAddress = 10.0.1.2
MACAddress = b4:43:0d:b6:0a:31
Device = 0x2737
Timeout = 10
Type = RM2

bash-3.2$ ll settings.ini
-rw-r--r--  1 radinsky  wheel  198 Jun 11 21:09 settings.ini
bash-3.2$ python server.py
Reading device configuration for RM2Salon
Beginning device auto-detection ...
Traceback (most recent call last):
  File "server.py", line 411, in <module>
    device.hostname = socket.gethostbyaddr(device.host[0])[0]
socket.herror: [Errno 1] Unknown host
bash-3.2$ cat settings.ini
bash-3.2$ ll settings.ini
-rw-r--r--  1 radinsky  wheel  0 Jun 11 21:10 settings.ini
bash-3.2$
radinsky commented 6 years ago

with Autodetect = 0 (or removing it at all) allows running the server, I've tested some basic learn/send of IR command, but something went wrong with the RF, not sure (not 100% trust the remote control I used), get temperature of RM2, but not tried the A1. @uudruid74 can please you take a look on autodetect?

uudruid74 commented 6 years ago

Yes, there is an issue (even before), where if something went wrong while it wrote to the settings.ini file, it leaves it blank. I'm going to add some code later tonight that wraps it in a try/except block to make a backup. It would suck to lose all the learned commands and such.

The Autodetect error is because it wants a name for your device and the default is to look it up by hostname. If you put an entry in /etc/hosts it should work. I'll add another try/except to catch the name resolution failure and come up with a better default, likely "Broadlink" + DeviceType, which would match the old device naming convention.

uudruid74 commented 6 years ago

@radinsky The cleanup of error handling should fix all the issues you found and a few more. I specifically took my device out of my local DNS to test autodetect. Using the new fallback code for device naming, it shows up as "BroadlinkRM2". If it's in DNS or /etc/hosts it shows up as it's hostname. It's not exactly matching the original, but there is no reason why you can't name it whatever you want in the settings.ini file.

The Macro language and IFTTT support (via POST/password) makes it real easy to integrate Google Assistant and have the assistant do anything that a person could do with a pile of remotes, plus I have my HTML5 based remote on my phone (still using GET on the local network).

radinsky commented 6 years ago

hey! the autodetect now works perfect! it's a massive improvement and saving annoying manual settings.

i did some tests for a1 as well and i got the temperature ok with /a1/temperature but all other sensors like lights, noise and etc not worked for me. tried to do it with getSensor and providing the name of sensor and device as appears in settings.ini BroadlinkA1. do you know how it should go now, i would like update the methods in README as well.

uudruid74 commented 6 years ago

OK - I see what happened. I don't have an A1 device to test and kinda mangled the test for old syntax compatibility. Your /a1/temperature URL was likely reading the broadlinkRM or whatever your first device was. It assumed that if you don't use the new syntax, you must want the first device on the list. In this case, the a1 means we should search for an A1 device. Another bug was likely stopping the other syntax from working.

/a1/sensorname = should hopefully find your A1 section by whatever name (search by type) and get the sensor value /SomeNamedDevice/getSensor/sensorname = the new syntax, allowing for multiple A1s or sensors on other devices.

If either of those don't work, let me know and I'll see if I can figure it out. It's tough without the device! And the way it's written now, the AUTH is done immediately, so I can't even pretend I have one. The code would abort immediately. This should be in the README (I hope - been a long week!)

All responses are JSON now (as opposed to only some). Hope that doesn't break anything. I needed it for AJAX compatibility.

radinsky commented 6 years ago

using /BroadlinkA1/getSensor/noise I'm getting:


[13/Jun/2018 19:41:17] "GET /BroadlinkA1/getSensor/noise HTTP/1.1" 200 -
----------------------------------------
Exception happened during processing of request from ('10.0.1.41', 59683)
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 295, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 321, in process_request
    self.finish_request(request, client_address)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 334, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 655, in __init__
    self.handle()
  File "server.py", line 44, in handle
    self.handle_one_request()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/BaseHTTPServer.py", line 328, in handle_one_request
    method()
  File "server.py", line 60, in do_GET
    self.messageHandler()
  File "server.py", line 189, in messageHandler
    result = getSensor(sensor, deviceName)
  File "server.py", line 368, in getSensor
    return result[sensor]
NameError: global name 'sensor' is not defined
----------------------------------------
radinsky commented 6 years ago

and /a1/temperature also stopped working:


[13/Jun/2018 19:43:02] "GET /a1/temperature HTTP/1.1" 200 -
----------------------------------------
Exception happened during processing of request from ('10.0.1.41', 59698)
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 295, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 321, in process_request
    self.finish_request(request, client_address)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 334, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/SocketServer.py", line 655, in __init__
    self.handle()
  File "server.py", line 44, in handle
    self.handle_one_request()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/BaseHTTPServer.py", line 328, in handle_one_request
    method()
  File "server.py", line 60, in do_GET
    self.messageHandler()
  File "server.py", line 184, in messageHandler
    if "A1" == paths[2].upper[2]:
TypeError: 'builtin_function_or_method' object has no attribute '__getitem__'
----------------------------------------
uudruid74 commented 6 years ago

@radinsky The first was because I used sensor instead of sensorName, the second is due to missing parenthesis after upper(). Oops! Silly mistakes. Sorry. Last commit should (hopefully) have it fixed! cross-fingers

radinsky commented 6 years ago

yeah, all looks perfect now, did some brief testing all looks stable. need to enhance a bit the error handling one day.. i will update the README with all new/updated calls and examples. thanks!!

uudruid74 commented 6 years ago

This actually fixes just about every open issue right now. I'm testing a new branch (basics working) with more error handling and the ability to do conditionals in macros, send WOL packets, test if hosts are up, etc.