Closed turboc1208 closed 7 years ago
Actually I have been thinking about making it both 2 and 3 compatible, I can work on that next.
I pushed a commit for python3 compatibility; its working for me on both python 2.7.12 and 3.6.0
Any idea what a RobotError -32600 error is?
I’m trying to get my password. Here is the code I’m running.
(hass) hass@hass:~/code/pyirobot$ cat GetPassword.py
from pyirobot import Robot
import json
print(Robot.GetPassword("192.168.2.116"))
(hass) hass@hass:~/code/pyirobot$
I noticed out on your github page, you seem to be interested in home automation. What systems are you running?
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Tuesday, January 17, 2017 at 10:19 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
I pushed a commit for python3 compatibility; its working for me on both python 2.7.12 and 3.6.0
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
I just duplicated your error with my robot here; its working in python2.7 but fails like this with python 3.6. I think I remember getting this error when the order of the keys in the JSON sent to the robot was incorrect, it's very sensitive about the payload you send. I'll look into this more.
I have quite a few Insteon smart switches and plug in modules in my house, along with the Insteon hub as well as the ISY 994i. I also have Kevo locks on my doors, and several Echo Dot scattered around the house to control everything via voice. I'm considering a Logitech Harmony Hub to integrate IR beaming into my automation. I've also got a number of custom built devices that I've put together with microcontrollers or raspberry pi, like a controller to turn on the electric blanket or sense when the dryer is done.
I'm very intrigued by SmartThings, they seem to have a pretty good ecosystem and a lot of community support, but I've held off because it is so cloud driven, and much of it stops working if your internet is flakey or down.
Thanks,
The cloud based issue is something that bothers me too. I have probably about a dozen GE smart switches and outlets at this point along with a couple of those in-wall switch modules that let you keep your existing switches. I use Wink for my primary hub to tie items together. I’ve got several of the Dots too. My kids are all older, so I setup one for each of them in their rooms. That way they can control their skills, alarms, etc individually, but still have access to the devices throughout the Wink Alexa skill. SmartThings does have a good community. I settled on Home Assistant though. The group over there is very helpful too, and with the AppDaemon addon, I can use Python to put in more advanced automations than the base application lets me do. It can also act as a hub which would remove me from the web even more.
I just got through paying for Christmas and Spring tuition for two of my boys so I’m holding off on buying the next round of items for a few months. Giving the checkbook a chance to recover. J
Thanks again
Chip
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Wednesday, January 18, 2017 at 7:26 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
I just duplicated your error with my robot here; its working in python2.7 but fails like this with python 3.6. I think I remember getting this error when the order of the keys in the JSON sent to the robot was incorrect, it's very sensitive about the payload you send. I'll look into this more.
I have quite a few Insteon smart switches and plug in modules in my house, along with the Insteon hub as well as the ISY 994i. I also have Kevo locks on my doors, and several Echo Dot scattered around the house to control everything via voice. I'm considering a Logitech Harmony Hub to integrate IR beaming into my automation. I've also got a number of custom built devices that I've put together with microcontrollers or raspberry pi, like a controller to turn on the electric blanket or sense when the dryer is done.
I'm very intrigued by SmartThings, they seem to have a pretty good ecosystem and a lot of community support, but I've held off because it is so cloud driven, and much of it stops working if your internet is flakey or down.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
I think I have this fixed now, there was a small difference in how different python versions were serializing dictionaries to JSON. I enforced the ordering now so that the robot always gets the request that it expects. I think this will work now with any 2.7 or 3.x python.
That sounds like a nice setup, I need to look at Home Assistant some more myself. I have a raspberry pi that would be perfect for that.
Can you send me a copy of a program running GetPassword that works? When I run mine I’m getting a bunch of no route to host type messages, but I know the IP address is right. The gateway and network mask are what you would think 192.168.2.1. and 255.255.255.0.
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Wednesday, January 18, 2017 at 10:34 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
I think I have this fixed now, there was a small difference in how different python versions were serializing dictionaries to JSON. I enforced the ordering now so that the robot always gets the request that it expects. I think this will work now with any 2.7 or 3.x python.
That sounds like a nice setup, I need to look at Home Assistant some more myself. I have a raspberry pi that would be perfect for that.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
Maybe your robot got a new DHCP address?
(pyirobot3) carl@carl-macbook ~/Dropbox/projects/pyirobot :) python asdf.py
Querying robot password...
xxxxxxxxxxxxxxxxxxx
Querying robot status...
Bin is full!
{
"cleaningPreferences": {
"lang": 0,
"timezone": "US/Pacific",
"name": "Roomba",
"carpetBoost": "Auto",
"cleaningPasses": "Auto",
"finishWhenBinFull": "Off",
"edgeClean": "On"
},
"mission": {
"batteryPercentage": 100,
"missionElapsedMinutes": 87,
"readyStatus": "BinFull",
"robotPosition": {
"theta": 46,
"point": {
"x": 816,
"y": -667
}
},
"missionCoveredSquareFootage": 580,
"binStatus": "Full",
"robotStatus": "Charging"
}
}
(pyirobot3) carl@carl-macbook ~/Dropbox/Carl/projects/pyirobot :) python --version
Python 3.6.0
(pyirobot3) carl@carl-macbook ~/Dropbox/Carl/projects/pyirobot :) cat asdf.py
#!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus
import json
robot_ip = "192.168.1.103"
print("Querying robot password...")
robot_password = Robot.GetPassword(robot_ip)
print(robot_password)
print()
print("Querying robot status...")
r = Robot(robot_ip, robot_password)
status = r.GetStatus()
if status["mission"]["binStatus"] == BinStatus.Full:
print("Bin is full!")
print()
print(json.dumps(status, indent=4))
I thought about the DHCP address, but It’s a reserved address in DHCP, and the iPhone app agrees on the IP address, and the Iphone is able to talk to it fine.
I’ll give the code you sent me a try tonight when I get home from work. There are a couple differences in what I was importing that might make a difference.
Thanks again for the help.
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Thursday, January 19, 2017 at 11:40 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
Maybe your robot got a new DHCP address? (pyirobot3) carl@carl-macbook ~/Dropbox/projects/pyirobot :) python asdf.py Querying robot password... xxxxxxxxxxxxxxxxxxx
Querying robot status... Bin is full!
{ "cleaningPreferences": { "lang": 0, "timezone": "US/Pacific", "name": "Roomba", "carpetBoost": "Auto", "cleaningPasses": "Auto", "finishWhenBinFull": "Off", "edgeClean": "On" }, "mission": { "batteryPercentage": 100, "missionElapsedMinutes": 87, "readyStatus": "BinFull", "robotPosition": { "theta": 46, "point": { "x": 816, "y": -667 } }, "missionCoveredSquareFootage": 580, "binStatus": "Full", "robotStatus": "Charging" } } (pyirobot3) carl@carl-macbook ~/Dropbox/Carl/projects/pyirobot :) python --version Python 3.6.0 (pyirobot3) carl@carl-macbook ~/Dropbox/Carl/projects/pyirobot :) cat asdf.py
from pyirobot import Robot, RobotError, BinStatus import json
robot_ip = "192.168.1.103"
print("Querying robot password...") robot_password = Robot.GetPassword(robot_ip) print(robot_password) print()
print("Querying robot status...") r = Robot(robot_ip, robot_password) status = r.GetStatus() if status["mission"]["binStatus"] == BinStatus.Full: print("Bin is full!") print()
print(json.dumps(status, indent=4))
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
Ok, I’m making progress.
I got the password. I had to beef up the headings. I looked at https://github.com/koalazak/dorita980/blob/master/bin/getpassword.js to see what he did.
So the next thing I did was remove the getpassword part of the program and just try to get the robot status. I’m having problems there. Is there anything I need to do to the password other than just cut and paste it in where the password was being pulled in from GetPassword? What I have is below.
from pyirobot import Robot, RobotError, BinStatus
import json
robot_ip = "192.168.2.116"
print("Querying robot password...")
robot_password = "thisismypassword"
print("Querying robot status...")
r = Robot(robot_ip, robot_password)
status = r.GetStatus()
if status["mission"]["binStatus"] == BinStatus.Full:
print("Bin is full!")
print()
print(json.dumps(status, indent=4))
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Thursday, January 19, 2017 at 11:40 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
from pyirobot import Robot, RobotError, BinStatus import json
robot_ip = "192.168.1.103"
print("Querying robot password...") robot_password = Robot.GetPassword(robot_ip) print(robot_password) print()
print("Querying robot status...") r = Robot(robot_ip, robot_password) status = r.GetStatus() if status["mission"]["binStatus"] == BinStatus.Full: print("Bin is full!") print()
print(json.dumps(status, indent=4))
That should work fine. Maybe you need to add those additional headers to _PostToRobot as well. What headers did you have add?
On Jan 20, 2017, at 8:35 PM, turboc1208 notifications@github.com wrote:
Ok, I’m making progress.
I got the password. I had to beef up the headings. I looked at https://github.com/koalazak/dorita980/blob/master/bin/getpassword.js to see what he did.
So the next thing I did was remove the getpassword part of the program and just try to get the robot status. I’m having problems there. Is there anything I need to do to the password other than just cut and paste it in where the password was being pulled in from GetPassword? What I have is below.
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus
import json
robot_ip = "192.168.2.116"
print("Querying robot password...")
robot_password = "thisismypassword"
print("Querying robot status...")
r = Robot(robot_ip, robot_password)
status = r.GetStatus()
if status["mission"]["binStatus"] == BinStatus.Full:
print("Bin is full!")
print()
print(json.dumps(status, indent=4))
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Thursday, January 19, 2017 at 11:40 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus import json
robot_ip = "192.168.1.103"
print("Querying robot password...") robot_password = Robot.GetPassword(robot_ip) print(robot_password) print()
print("Querying robot status...") r = Robot(robot_ip, robot_password) status = r.GetStatus() if status["mission"]["binStatus"] == BinStatus.Full: print("Bin is full!") print()
print(json.dumps(status, indent=4))
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub, or mute the thread.
I also had to specify port 443. I probably could/should have done that just passing it in as part of the robotIP, but this works for now to get the password.
def GetPassword(robotIP):
"""
Get the password for this robot
Before calling this method, place the robot on its dock and then hold down the home button for 3-4 seconds,
until the LEDs illuminate and the robot emits a series of tones. Then quickly call this method
Returns:
The robot password (str)
"""
result = requests.post("https://{}:443/umi".format(robotIP),
data=json.dumps({"do" : "get",
"args" : ["passwd"],
"id" : 0}),
headers={"Content-Type" : "application/json",
"connection":"close",
"user-agent":"aspen%20production/2618 CFNetwork/758.3.15 Darwin/15.4.0",
"Content-Encoding":"identity",
"Accept":"*/*",
"Accept-Language":"en-us",
"Host":robotIP},
verify=False)
res = result.json()
if "err" in res:
raise RobotError(res["err"])
return res["ok"]["passwd"]
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Friday, January 20, 2017 at 10:47 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
That should work fine. Maybe you need to add those additional headers to _PostToRobot as well. What headers did you have add?
On Jan 20, 2017, at 8:35 PM, turboc1208 notifications@github.com wrote:
Ok, I’m making progress.
I got the password. I had to beef up the headings. I looked at https://github.com/koalazak/dorita980/blob/master/bin/getpassword.js to see what he did.
So the next thing I did was remove the getpassword part of the program and just try to get the robot status. I’m having problems there. Is there anything I need to do to the password other than just cut and paste it in where the password was being pulled in from GetPassword? What I have is below.
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus
import json
robot_ip = "192.168.2.116"
print("Querying robot password...")
robot_password = "thisismypassword"
print("Querying robot status...")
r = Robot(robot_ip, robot_password)
status = r.GetStatus()
if status["mission"]["binStatus"] == BinStatus.Full:
print("Bin is full!")
print()
print(json.dumps(status, indent=4))
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Thursday, January 19, 2017 at 11:40 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus import json
robot_ip = "192.168.1.103"
print("Querying robot password...") robot_password = Robot.GetPassword(robot_ip) print(robot_password) print()
print("Querying robot status...") r = Robot(robot_ip, robot_password) status = r.GetStatus() if status["mission"]["binStatus"] == BinStatus.Full: print("Bin is full!") print()
print(json.dumps(status, indent=4))
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub, or mute the thread.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
Wondering something else.
To get the password, I had to hit the home button till the music played. That in effect wakes up the unit. Do I have to do something else with the unit on the docking station to wake it up to get the code to run using the password?
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Friday, January 20, 2017 at 10:47 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
That should work fine. Maybe you need to add those additional headers to _PostToRobot as well. What headers did you have add?
On Jan 20, 2017, at 8:35 PM, turboc1208 notifications@github.com wrote:
Ok, I’m making progress.
I got the password. I had to beef up the headings. I looked at https://github.com/koalazak/dorita980/blob/master/bin/getpassword.js to see what he did.
So the next thing I did was remove the getpassword part of the program and just try to get the robot status. I’m having problems there. Is there anything I need to do to the password other than just cut and paste it in where the password was being pulled in from GetPassword? What I have is below.
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus
import json
robot_ip = "192.168.2.116"
print("Querying robot password...")
robot_password = "thisismypassword"
print("Querying robot status...")
r = Robot(robot_ip, robot_password)
status = r.GetStatus()
if status["mission"]["binStatus"] == BinStatus.Full:
print("Bin is full!")
print()
print(json.dumps(status, indent=4))
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Thursday, January 19, 2017 at 11:40 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus import json
robot_ip = "192.168.1.103"
print("Querying robot password...") robot_password = Robot.GetPassword(robot_ip) print(robot_password) print()
print("Querying robot status...") r = Robot(robot_ip, robot_password) status = r.GetStatus() if status["mission"]["binStatus"] == BinStatus.Full: print("Bin is full!") print()
print(json.dumps(status, indent=4))
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub, or mute the thread.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
I haven't had to do that but it's worth a shot to see what happens for you.
What firmware version is your robot running? Mine is on 1.6.6, I've heard there is an update rolling out that my robot hasn't picked up yet. In the app it's under Settings -> About Roomba
Carl
Cheers, Carl
On Jan 21, 2017, at 5:38 AM, turboc1208 notifications@github.com wrote:
Wondering something else.
To get the password, I had to hit the home button till the music played. That in effect wakes up the unit. Do I have to do something else with the unit on the docking station to wake it up to get the code to run using the password?
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Friday, January 20, 2017 at 10:47 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
That should work fine. Maybe you need to add those additional headers to _PostToRobot as well. What headers did you have add?
On Jan 20, 2017, at 8:35 PM, turboc1208 notifications@github.com wrote:
Ok, I’m making progress.
I got the password. I had to beef up the headings. I looked at https://github.com/koalazak/dorita980/blob/master/bin/getpassword.js to see what he did.
So the next thing I did was remove the getpassword part of the program and just try to get the robot status. I’m having problems there. Is there anything I need to do to the password other than just cut and paste it in where the password was being pulled in from GetPassword? What I have is below.
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus
import json
robot_ip = "192.168.2.116"
print("Querying robot password...")
robot_password = "thisismypassword"
print("Querying robot status...")
r = Robot(robot_ip, robot_password)
status = r.GetStatus()
if status["mission"]["binStatus"] == BinStatus.Full:
print("Bin is full!")
print()
print(json.dumps(status, indent=4))
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Thursday, January 19, 2017 at 11:40 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus import json
robot_ip = "192.168.1.103"
print("Querying robot password...") robot_password = Robot.GetPassword(robot_ip) print(robot_password) print()
print("Querying robot status...") r = Robot(robot_ip, robot_password) status = r.GetStatus() if status["mission"]["binStatus"] == BinStatus.Full: print("Bin is full!") print()
print(json.dumps(status, indent=4))
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub, or mute the thread.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub, or mute the thread.
1.6.6 dated Nov 23, 2016
I may have my versions wrong, but this is not the top of the line 900 series (980 I think), it’s the second one down ( the 960) I think. But everything seems to talk about these as being a 900 series, so like you said in your documentation, it “should” work.
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Saturday, January 21, 2017 at 1:14 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
I haven't had to do that but it's worth a shot to see what happens for you.
What firmware version is your robot running? Mine is on 1.6.6, I've heard there is an update rolling out that my robot hasn't picked up yet. In the app it's under Settings -> About Roomba
Carl
Cheers, Carl
On Jan 21, 2017, at 5:38 AM, turboc1208 notifications@github.com wrote:
Wondering something else.
To get the password, I had to hit the home button till the music played. That in effect wakes up the unit. Do I have to do something else with the unit on the docking station to wake it up to get the code to run using the password?
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Friday, January 20, 2017 at 10:47 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
That should work fine. Maybe you need to add those additional headers to _PostToRobot as well. What headers did you have add?
On Jan 20, 2017, at 8:35 PM, turboc1208 notifications@github.com wrote:
Ok, I’m making progress.
I got the password. I had to beef up the headings. I looked at https://github.com/koalazak/dorita980/blob/master/bin/getpassword.js to see what he did.
So the next thing I did was remove the getpassword part of the program and just try to get the robot status. I’m having problems there. Is there anything I need to do to the password other than just cut and paste it in where the password was being pulled in from GetPassword? What I have is below.
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus
import json
robot_ip = "192.168.2.116"
print("Querying robot password...")
robot_password = "thisismypassword"
print("Querying robot status...")
r = Robot(robot_ip, robot_password)
status = r.GetStatus()
if status["mission"]["binStatus"] == BinStatus.Full:
print("Bin is full!")
print()
print(json.dumps(status, indent=4))
From: Carl Seelye notifications@github.com Reply-To: cseelye/pyirobot reply@reply.github.com Date: Thursday, January 19, 2017 at 11:40 PM To: cseelye/pyirobot pyirobot@noreply.github.com Cc: Chip Cox chip.cox@coxdogs.com, Author author@noreply.github.com Subject: Re: [cseelye/pyirobot] Python3 (#1)
!/usr/bin/env python3
from pyirobot import Robot, RobotError, BinStatus import json
robot_ip = "192.168.1.103"
print("Querying robot password...") robot_password = Robot.GetPassword(robot_ip) print(robot_password) print()
print("Querying robot status...") r = Robot(robot_ip, robot_password) status = r.GetStatus() if status["mission"]["binStatus"] == BinStatus.Full: print("Bin is full!") print()
print(json.dumps(status, indent=4))
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub, or mute the thread.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub, or mute the thread.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
Any chance of getting this ported to python3?