TomFaulkner / SenseMe

Python Library for Haiku SenseMe app controlled fans/lights
GNU General Public License v3.0
21 stars 10 forks source link

Remove requirement on RegEx #5

Open TomFaulkner opened 7 years ago

TomFaulkner commented 7 years ago

The regex call, which appears in both discovery functions, is unnecessary, switch to split on ;

shepherdjay commented 5 years ago

I would be willing to take this up, do you have an example string you are currently running the regex against so I can write a test?

TomFaulkner commented 5 years ago

That's a good question. I can see where the three are, but as for the string, I don't think I have an example of that at the moment. Do you have a device to run it on? If so, you could run a few commands and log the output (might have to add logging for that) to see them.

Tests are good, I've been meaning to add unit tests for some time!

If you don't have a device, I can get back to you in a couple of days with some strings.

shepherdjay commented 5 years ago

I don't have a device myself, but can get started on the code changes I would intend to make and just insert whatever test strings you get back to me with into the test. Since you don't have a test framework yet for this project am I good implementing in pytest or would you prefer the built in unittest? I can do it in either.

TomFaulkner commented 5 years ago

I have been meaning to try pytest. Briefly looking at the docs for it I think that will be fine.

There isn't a linter config, but I am using black's autoformatting. (https://github.com/ambv/black)

TomFaulkner commented 5 years ago

Sorry about the delay on the test strings!

Here is a bunch!

>>> f = discover()[0]
DEBUG:senseme.senseme:Sending broadcast.
DEBUG:senseme.senseme:Listening...
INFO:senseme.senseme:Received a message
DEBUG:senseme.senseme:time_to_wait exceeded or devices_to_find met
>>> f
SenseMe(name='Living Room Fan', ip='192.168.1.50', model='FAN,HAIKU', series='HSERIES', mac='20:F8:5E:D8:A2:74')
>>> f.speed
INFO:senseme.senseme:Status: (Living Room Fan;FAN;SPD;ACTUAL;7)
INFO:senseme.senseme:(Living Room Fan;FAN;SPD;ACTUAL;7)
DEBUG:senseme.senseme:7
7
>>> f.brightness
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;LEVEL;ACTUAL;0)
INFO:senseme.senseme:(Living Room Fan;LIGHT;LEVEL;ACTUAL;0)
0
>>> f.light_toggle()
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;PWR;OFF)
INFO:senseme.senseme:(Living Room Fan;LIGHT;PWR;OFF)

>>> f.whoosh()
DEBUG:root:Ran function
INFO:senseme.senseme:Status: (Living Room Fan;DEVICE;BEEPER;ON)
INFO:senseme.senseme:(Living Room Fan;DEVICE;BEEPER;ON)
INFO:senseme.senseme:Status: (Living Room Fan;DEVICE;INDICATORS;ON)
INFO:senseme.senseme:(Living Room Fan;DEVICE;INDICATORS;ON)
INFO:senseme.senseme:Status: (Living Room Fan;DEVICE;LIGHT;PRESENT)
INFO:senseme.senseme:(Living Room Fan;DEVICE;LIGHT;PRESENT)
INFO:senseme.senseme:Status: (Living Room Fan;DEVICE;SERVER;PRODUCTION)
INFO:senseme.senseme:(Living Room Fan;DEVICE;SERVER;PRODUCTION)
INFO:senseme.senseme:Status: (Living Room Fan;ERRORLOG;ENTRIES;NUM;10)
INFO:senseme.senseme:(Living Room Fan;ERRORLOG;ENTRIES;NUM;10)
INFO:senseme.senseme:Status: (Living Room Fan;ERRORLOG;ENTRIES;MAX;10)
INFO:senseme.senseme:(Living Room Fan;ERRORLOG;ENTRIES;MAX;10)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;AUTO;ON)
INFO:senseme.senseme:(Living Room Fan;FAN;AUTO;ON)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;DIR;FWD)
INFO:senseme.senseme:(Living Room Fan;FAN;DIR;FWD)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;PWR;ON)
INFO:senseme.senseme:(Living Room Fan;FAN;PWR;ON)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;SPD;ACTUAL;7)
INFO:senseme.senseme:(Living Room Fan;FAN;SPD;ACTUAL;7)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;SPD;MAX;7)
INFO:senseme.senseme:(Living Room Fan;FAN;SPD;MAX;7)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;SPD;MIN;1)
INFO:senseme.senseme:(Living Room Fan;FAN;SPD;MIN;1)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;BOOKENDS;1;7)
INFO:senseme.senseme:(Living Room Fan;FAN;BOOKENDS;1;7)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;TIMER;CURR;0)
INFO:senseme.senseme:(Living Room Fan;FAN;TIMER;CURR;0)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;TIMER;MAX;7)
INFO:senseme.senseme:(Living Room Fan;FAN;TIMER;MAX;7)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;TIMER;MIN;1)
INFO:senseme.senseme:(Living Room Fan;FAN;TIMER;MIN;1)
INFO:senseme.senseme:Status: (Living Room Fan;FAN;WHOOSH;STATUS;OFF)
INFO:senseme.senseme:(Living Room Fan;FAN;WHOOSH;STATUS;OFF)
INFO:senseme.senseme:Status: (Living Room Fan;FW;NAME;FW000007)
INFO:senseme.senseme:(Living Room Fan;FW;NAME;FW000007)
INFO:senseme.senseme:Status: (Living Room Fan;FW;FW000007;2.5.0)
INFO:senseme.senseme:(Living Room Fan;FW;FW000007;2.5.0)
INFO:senseme.senseme:Status: (Living Room Fan;GROUP;LIST;Living Room)
INFO:senseme.senseme:(Living Room Fan;GROUP;LIST;Living Room)
INFO:senseme.senseme:Status: (Living Room Fan;GROUP;ROOM;TYPE;6)
INFO:senseme.senseme:(Living Room Fan;GROUP;ROOM;TYPE;6)
INFO:senseme.senseme:Status: (Living Room Fan;LEARN;MAXSPEED;7)
INFO:senseme.senseme:(Living Room Fan;LEARN;MAXSPEED;7)
INFO:senseme.senseme:Status: (Living Room Fan;LEARN;MINSPEED;0)
INFO:senseme.senseme:(Living Room Fan;LEARN;MINSPEED;0)
INFO:senseme.senseme:Status: (Living Room Fan;LEARN;ZEROTEMP;2000)
INFO:senseme.senseme:(Living Room Fan;LEARN;ZEROTEMP;2000)
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;AUTO;ON)
INFO:senseme.senseme:(Living Room Fan;LIGHT;AUTO;ON)
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;LEVEL;ACTUAL;0)
INFO:senseme.senseme:(Living Room Fan;LIGHT;LEVEL;ACTUAL;0)
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;BOOKENDS;1;16)
INFO:senseme.senseme:(Living Room Fan;LIGHT;BOOKENDS;1;16)
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;LEVEL;MAX;16)
INFO:senseme.senseme:(Living Room Fan;LIGHT;LEVEL;MAX;16)
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;LEVEL;MIN;1)
INFO:senseme.senseme:(Living Room Fan;LIGHT;LEVEL;MIN;1)
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;PWR;OFF)
INFO:senseme.senseme:(Living Room Fan;LIGHT;PWR;OFF)
INFO:senseme.senseme:Status: (Living Room Fan;LIGHT;BOOKENDS;1;16)
INFO:senseme.senseme:(Living Room Fan;LIGHT;BOOKENDS;1;16)
INFO:senseme.senseme:Status: (Living Room Fan;NAME;VALUE;Living Room Fan)
INFO:senseme.senseme:(Living Room Fan;NAME;VALUE;Living Room Fan)
INFO:senseme.senseme:Status: (Living Room Fan;NW;AP;STATUS;OFF)
INFO:senseme.senseme:(Living Room Fan;NW;AP;STATUS;OFF)
INFO:senseme.senseme:Status: (Living Room Fan;NW;DHCP;OFF)
INFO:senseme.senseme:(Living Room Fan;NW;DHCP;OFF)
INFO:senseme.senseme:Status: (Living Room Fan;NW;PARAMS;ACTUAL;192.168.1.50;255.255.255.0;192.168.1.1)
INFO:senseme.senseme:(Living Room Fan;NW;PARAMS;ACTUAL;192.168.1.50;255.255.255.0;192.168.1.1)
INFO:senseme.senseme:Status: (Living Room Fan;NW;SSID;sendratan)
INFO:senseme.senseme:(Living Room Fan;NW;SSID;sendratan)
INFO:senseme.senseme:Status: (Living Room Fan;NW;TOKEN;90000459-db50-42f2-96d2-74739961c961)
INFO:senseme.senseme:(Living Room Fan;NW;TOKEN;90000459-db50-42f2-96d2-74739961c961)
INFO:senseme.senseme:Status: (Living Room Fan;SCHEDULE;CAP;MAX EVENTS,29)
INFO:senseme.senseme:(Living Room Fan;SCHEDULE;CAP;MAX EVENTS,29)
INFO:senseme.senseme:Status: (Living Room Fan;SCHEDULE;EVENT;LIST;NONE)
INFO:senseme.senseme:(Living Room Fan;SCHEDULE;EVENT;LIST;NONE)
INFO:senseme.senseme:Status: (Living Room Fan;SLEEP;EVENT;OFF;LIGHT,LEVEL,12)
INFO:senseme.senseme:(Living Room Fan;SLEEP;EVENT;OFF;LIGHT,LEVEL,12)
INFO:senseme.senseme:Status: (Living Room Fan;SLEEP;EVENT;ON)
INFO:senseme.senseme:(Living Room Fan;SLEEP;EVENT;ON)
INFO:senseme.senseme:Status: (Living Room Fan;SLEEP;STATE;OFF)
INFO:senseme.senseme:(Living Room Fan;SLEEP;STATE;OFF)
INFO:senseme.senseme:Status: (Living Room Fan;SMARTMODE;ACTUAL;OFF)
INFO:senseme.senseme:(Living Room Fan;SMARTMODE;ACTUAL;OFF)
INFO:senseme.senseme:Status: (Living Room Fan;SMARTMODE;STATE;OFF)
INFO:senseme.senseme:(Living Room Fan;SMARTMODE;STATE;OFF)
INFO:senseme.senseme:Status: (Living Room Fan;SMARTSLEEP;IDEALTEMP;2111)
INFO:senseme.senseme:(Living Room Fan;SMARTSLEEP;IDEALTEMP;2111)
INFO:senseme.senseme:Status: (Living Room Fan;SMARTSLEEP;MAXSPEED;7)
INFO:senseme.senseme:(Living Room Fan;SMARTSLEEP;MAXSPEED;7)
INFO:senseme.senseme:Status: (Living Room Fan;SMARTSLEEP;MINSPEED;0)
INFO:senseme.senseme:(Living Room Fan;SMARTSLEEP;MINSPEED;0)
INFO:senseme.senseme:Status: (Living Room Fan;SNSROCC;TIMEOUT;CURR;3600000)
INFO:senseme.senseme:(Living Room Fan;SNSROCC;TIMEOUT;CURR;3600000)
INFO:senseme.senseme:Status: (Living Room Fan;SNSROCC;TIMEOUT;MAX;86400000)
INFO:senseme.senseme:(Living Room Fan;SNSROCC;TIMEOUT;MAX;86400000)
INFO:senseme.senseme:Status: (Living Room Fan;SNSROCC;TIMEOUT;MIN;60000)
INFO:senseme.senseme:(Living Room Fan;SNSROCC;TIMEOUT;MIN;60000)
INFO:senseme.senseme:Status: (Living Room Fan;TIME;VALUE;2018-10-10T23:20:19Z)
INFO:senseme.senseme:(Living Room Fan;TIME;VALUE;2018-10-10T23:20:19Z)
INFO:senseme.senseme:Status: (Living Room Fan;WINTERMODE;HEIGHT;213)
INFO:senseme.senseme:(Living Room Fan;WINTERMODE;HEIGHT;213)
INFO:senseme.senseme:Status: (Living Room Fan;WINTERMODE;STATE;OFF)
INFO:senseme.senseme:(Living Room Fan;WINTERMODE;STATE;OFF)
INFO:senseme.senseme:Socket Timed Out
shepherdjay commented 5 years ago

Apologize for the misunderstanding, do you have an example of the raw messages that are received on the buffer? The regex currently does a match for messages that include DEVICE;ID which makes me wonder if other types of messages are received.

So this line here: message = s.recvfrom(1024)

Maybe modify to add something that logs that message like this:

LOGGER.info("Received a message")
LOGGER.debug(f"Message: {message}")
TomFaulkner commented 5 years ago

Looking back at the code, you're right in that the examples I gave above aren't sufficient. The ones I gave are status updates, which don't have the same content as some of the other responses. The lines above are output from what is now line 1293, the _query method, on the master branch (which was updated last night).

The lines I think you are looking for is the discover and discover_single (which are sadly duplicate of one another.) I'll get back to you with output from those.

shepherdjay commented 5 years ago

Yeah looking to just get what the socket sees. So that s.recvfrom(1024) in discover being assigned to message is what will ultimately be passed to the new function to be decoded and parsed. I did see the master update which I'll have to pull in and fix conflicts before I make another push on the pull request. Depending on what those messages look like regex may still be the best way to parse it but this will at least refactor out the duplicate code.

shepherdjay commented 5 years ago

Here is a working refactored function example. Not sure if this is more readable than regex, let me know your thoughts:

def decode_discovery(message: Tuple[bytes, Tuple[str, str]]) -> Tuple:
    """ Takes a message in the return format of socket.recvfrom and returns decoded SenseMe discovery information """
    message_decoded = message[0].decode("utf-8")
    message_items = message_decoded.strip('()').split(';')
    name = message_items[0]
    mac = message_items[3]
    model, series = message_items[4].split(',')
    ip = message[1][0]
    return name, mac, model, series, ip
TomFaulkner commented 5 years ago

Sorry to not get back to you, as I said in the PR comments.

The code looks good, I'll have to compare it to a string from the fan. It will probably be next week when I can get them to you.