Python script for thinkingcleaner roomba interface

Posted on
Sat Feb 06, 2016 5:30 am
haavarda offline
User avatar
Posts: 702
Joined: Aug 18, 2012
Location: Norway

Python script for thinkingcleaner roomba interface

Since this topic not so related to the rP I created a separate topic for this.

Håvard

Posted on
Sat Feb 06, 2016 5:39 am
haavarda offline
User avatar
Posts: 702
Joined: Aug 18, 2012
Location: Norway

Re: Python script for thinkingcleaner roomba interface

Hi Karl.
I made som minor changes just ro reflect the variable name and python scirpt name that makes sense for me.
It works perfectly and the timestamp is updated every time the state of the roomba changes.

Code: Select all
# by Karl Wachs
# feb 5 
# try to get roomba command
##
import SocketServer
import datetime, subprocess, os, time
class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()

        #indigo.server.log( unicode(self.client_address))
        #indigo.server.log( unicode(self.data))
 
        if unicode(self.data).find("webhook.html")>-1:
            indigo.server.log( "roomba send command")
            indigo.variable.updateValue("RoombaUpdate",datetime.datetime.now().strftime("%Y%m%d-%H:%M:%S"))
 
        return   

def killOldPgm(myPID,pgmToKill):
        cmd= "ps -ef | grep "+pgmToKill+" | grep -v grep"
        ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()[0]
        lines=ret.split("\n")
        for line in lines:
            if len(line) < 10: continue
            line=line.split()
            pid=int(line[1])
            if pid == int(myPID): continue
            os.system("kill -9 "+str(pid))
            indigo.server.log( "killed program: "+pgmToKill+" pid="+str(pid))
            time.sleep(5)
 

if __name__ == "__main__":

    PORT = 6193                  ## port number used by roomba
    ipAddress="192.168.1.100"  ## ip number of your indigo MAC

    try:
        indigo.variable.create("RoombaUpdate","")
    except:
        pass

    myPID = str(os.getpid())
    indigo.server.log(" PID of roomba server is:" +myPID  )
    killOldPgm(myPID,"RoombaUpdate.py")
   
    # Create the server, binding on port xxx
    server = SocketServer.TCPServer((ipAddress, PORT), MyTCPHandler)

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    server.serve_forever()


When an update is detected and the variabel above is updated I am running a script that sends a request to the roomba for status update. This is handled in a new script. Could this script be implemented in your script and be the result of incoming web hook?

Code: Select all
import simplejson as json
import urllib2

urlReturned = urllib2.urlopen('http://192.168.1.114/status.json')

#format api returned data
data_getStatus = urlReturned.read()

#interpret json
dict = json.loads(data_getStatus)

action = str(dict['action'])
indigo.variable.updateValue(12824087, action)

result = str(dict['result'])
indigo.variable.updateValue(1042192793, result)

name = str(dict['status']['name'])
indigo.variable.updateValue(1267855651, name)

battery_charge = str(dict['status']['battery_charge'])
indigo.variable.updateValue(1262851281, battery_charge)

capacity = str(dict['status']['capacity'])
indigo.variable.updateValue(720495017, capacity)

cleaner_state = str(dict['status']['cleaner_state'])
indigo.variable.updateValue(1925578960, cleaner_state)


cleaning = str(dict['status']['cleaning'])
indigo.variable.updateValue(1995969993, cleaning)

schedule_serial_number = str(dict['status']['schedule_serial_number'])
indigo.variable.updateValue(1720655499, schedule_serial_number)

near_homebase = str(dict['status']['near_homebase'])
indigo.variable.updateValue(321117213, near_homebase)

translateDict = {'st_base': 'On homebase: Not Charging', 'st_base_recon': 'On homebase: Reconditioning Charging', 'st_base_full': 'On homebase: Full Charging', 'st_base_trickle': 'On homebase: Trickle Charging', 'st_base_wait': 'On homebase: Waiting', 'st_plug': 'Plugged in: Not Charging', 'st_plug_recon': 'Plugged in: Reconditioning Charging', 'st_plug_full': 'Plugged in: Full Charging', 'st_plug_trickle': 'Plugged in: Trickle Charging', 'st_plug_wait': 'Plugged in: Waiting', 'st_stopped': 'Stopped', 'st_clean': 'Cleaning', 'st_cleanstop': 'Stopped with cleaning', 'st_clean_spot': 'Spot cleaning', 'st_clean_max': 'Max cleaning', 'st_delayed': 'Delayed cleaning will start soon ..', 'st_dock': 'Searching Homebase', 'st_pickup': 'Roomba picked up', 'st_remote': 'Remote control driving', 'st_wait': 'Waiting for command', 'st_off': 'Off', 'st_error': 'Error', 'st_locate': 'Find me!', 'st_unknown': 'Unknown state'} 


translateFrom = indigo.variables[1925578960].value
translateTo = translateDict.get(translateFrom, 'error translation')

indigo.variable.updateValue(1017727089, value=translateTo)

Håvard

Posted on
Sat Feb 06, 2016 7:42 am
kw123 offline
User avatar
Posts: 8333
Joined: May 12, 2013
Location: Dallas, TX

Re: Python script for thinkingcleaner roomba interface

As for resources consumed: open activity monitor and look at the process with the pid in the logfile
It should be very small as it just sits there and waits
As for stopping:
Copy the part that kills the old process and put it into another action. This can be an internal script.
To handle the send part: we could add a curl statement that sends the commands to the roomba. I am on my iPhone and can't look at your code. Will check it later.


Sent from my iPhone using Tapatalk

Posted on
Sat Feb 06, 2016 8:48 am
kw123 offline
User avatar
Posts: 8333
Joined: May 12, 2013
Location: Dallas, TX

Re: Python script for thinkingcleaner roomba interface

This should do the whole thing:
Code: Select all
# by Karl Wachs
# feb 5 
# try to get roomba command
##
import SocketServer
import datetime, subprocess, os, time
import simplejson as json
import urllib2

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        # self.request is the TCP socket connected to the client
        self.data = self.request.recv(1024).strip()

        #indigo.server.log( unicode(self.client_address))
        #indigo.server.log( unicode(self.data))
 
        if unicode(self.data).find("webhook.html")>-1:
            indigo.server.log( "roomba send command")
            indigo.variable.updateValue("RoombaUpdate",datetime.datetime.now().strftime("%Y%m%d-%H:%M:%S"))

            urlReturned = urllib2.urlopen('http://192.168.1.114/status.json')

            #format api returned data
            data_getStatus = urlReturned.read()

            #interpret json
            dict = json.loads(data_getStatus)

            action = str(dict['action'])
            indigo.variable.updateValue(12824087, action)

            result = str(dict['result'])
            indigo.variable.updateValue(1042192793, result)

            name = str(dict['status']['name'])
            indigo.variable.updateValue(1267855651, name)

            battery_charge = str(dict['status']['battery_charge'])
            indigo.variable.updateValue(1262851281, battery_charge)

            capacity = str(dict['status']['capacity'])
            indigo.variable.updateValue(720495017, capacity)

            cleaner_state = str(dict['status']['cleaner_state'])
            indigo.variable.updateValue(1925578960, cleaner_state)


            cleaning = str(dict['status']['cleaning'])
            indigo.variable.updateValue(1995969993, cleaning)

            schedule_serial_number = str(dict['status']['schedule_serial_number'])
            indigo.variable.updateValue(1720655499, schedule_serial_number)

            near_homebase = str(dict['status']['near_homebase'])
            indigo.variable.updateValue(321117213, near_homebase)

            translateDict = {'st_base': 'On homebase: Not Charging', 'st_base_recon': 'On homebase: Reconditioning Charging', 'st_base_full': 'On homebase: Full Charging', 'st_base_trickle': 'On homebase: Trickle Charging', 'st_base_wait': 'On homebase: Waiting', 'st_plug': 'Plugged in: Not Charging', 'st_plug_recon': 'Plugged in: Reconditioning Charging', 'st_plug_full': 'Plugged in: Full Charging', 'st_plug_trickle': 'Plugged in: Trickle Charging', 'st_plug_wait': 'Plugged in: Waiting', 'st_stopped': 'Stopped', 'st_clean': 'Cleaning', 'st_cleanstop': 'Stopped with cleaning', 'st_clean_spot': 'Spot cleaning', 'st_clean_max': 'Max cleaning', 'st_delayed': 'Delayed cleaning will start soon ..', 'st_dock': 'Searching Homebase', 'st_pickup': 'Roomba picked up', 'st_remote': 'Remote control driving', 'st_wait': 'Waiting for command', 'st_off': 'Off', 'st_error': 'Error', 'st_locate': 'Find me!', 'st_unknown': 'Unknown state'} 


            translateFrom = indigo.variables[1925578960].value
            translateTo = translateDict.get(translateFrom, 'error translation')

            indigo.variable.updateValue(1017727089, value=translateTo)
        return
       
def killOldPgm(myPID,pgmToKill):
        cmd= "ps -ef | grep "+pgmToKill+" | grep -v grep"
        ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()[0]
        lines=ret.split("\n")
        for line in lines:
            if len(line) < 10: continue
            line=line.split()
            pid=int(line[1])
            if pid == int(myPID): continue
            os.system("kill -9 "+str(pid))
            indigo.server.log( "killed program: "+pgmToKill+" pid="+str(pid))
            time.sleep(5)
 

if __name__ == "__main__":

    PORT = 6193                  ## port number used by roomba
    ipAddress="192.168.1.100"  ## ip number of your indigo MAC

    try:
        indigo.variable.create("RoombaUpdate","")
    except:
        pass

    myPID = str(os.getpid())
    indigo.server.log(" PID of roomba server is:" +myPID  )
    killOldPgm(myPID,"RoombaUpdate.py")
   
    # Create the server, binding on port xxx
    server = SocketServer.TCPServer((ipAddress, PORT), MyTCPHandler)

    # Activate the server; this will keep running until you
    # interrupt the program with Ctrl-C
    server.serve_forever()


and put this into an action "killRomba" execute script, should not need to be external script, it executes FAST
Code: Select all
import subprocess
pgmToKill="roomba.py"
cmd= "ps -ef | grep "+pgmToKill+" | grep -v grep"
ret = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE).communicate()[0]
lines=ret.split("\n")
for line in lines:
    if len(line) < 10: continue
    line=line.split()
    pid=int(line[1])
    os.system("kill -9 "+str(pid))
    indigo.server.log( "killed program: "+pgmToKill+" pid="+str(pid))


Karl

Posted on
Sat Feb 06, 2016 11:19 am
haavarda offline
User avatar
Posts: 702
Joined: Aug 18, 2012
Location: Norway

Re: Python script for thinkingcleaner roomba interface

That is also working Karl. Thank you so much for your support. This roomba project is really starting to be fun now! :D

Håvard

Posted on
Sat Feb 06, 2016 11:23 am
kw123 offline
User avatar
Posts: 8333
Joined: May 12, 2013
Location: Dallas, TX

Re: Python script for thinkingcleaner roomba interface

good to hear.. I am just getting into socket communication.. struggling with threading and callbacks.. but that would open a host of options do do things much faster , more stable ..

karl
ps do you see any load from the program in activity log? should be minimal.

Posted on
Sat Feb 06, 2016 11:27 am
haavarda offline
User avatar
Posts: 702
Joined: Aug 18, 2012
Location: Norway

Re: Python script for thinkingcleaner roomba interface

Yepp that is indeed minimal... See the pictures below:
Attachments
Screen Shot 2016-02-06 at 18.24.57.png
Screen Shot 2016-02-06 at 18.24.57.png (65.93 KiB) Viewed 5528 times
Screen Shot 2016-02-06 at 18.24.38.png
Screen Shot 2016-02-06 at 18.24.38.png (58.96 KiB) Viewed 5528 times

Håvard

Posted on
Sat Feb 13, 2016 1:55 am
haavarda offline
User avatar
Posts: 702
Joined: Aug 18, 2012
Location: Norway

Re: Python script for thinkingcleaner roomba interface

Hi Karl.
I have been running this for some time now and I seams very stable. Is it difficult to collect this as a plugin, and start stop the server when the plugin is enabled/disabled? And in addition store the response as states in the plugin? I guess that if this would be a plugin then the IP address and listening port should be configurable also?

Håvard

Posted on
Sat Feb 13, 2016 10:27 am
kw123 offline
User avatar
Posts: 8333
Joined: May 12, 2013
Location: Dallas, TX

Re: Python script for thinkingcleaner roomba interface

Harvard,
sure its possible :D

but what can you NOT do with the 2 scripts:
1. start: yes= run the script
2. stop: yes= run the kill script
3. set IP# and PORT number dynamically: yes (read 2 variables from indigo )
3. set variables to values of roomba responses : yes
4. the only thing you can not do is change a state of a device.
what else is missing?

Writing a plugin would probably take a day with debugging. Happy to do that if there is a real "payback"(*) for it.

Karl

(*) functionality

Posted on
Sat Feb 13, 2016 11:31 am
haavarda offline
User avatar
Posts: 702
Joined: Aug 18, 2012
Location: Norway

Re: Python script for thinkingcleaner roomba interface

Hi Karl. I see your point. There is practically now gain from assembling this to a plugin.

Is there an elegant way to handle a state dependent timer. I want to pull the device every 30 minutes when in docking, and once a minute when leaving the base station.

I know I could do this with triggers and timers, but is it more effective to handle this in the script?

Håvard

Posted on
Sat Feb 13, 2016 12:41 pm
kw123 offline
User avatar
Posts: 8333
Joined: May 12, 2013
Location: Dallas, TX

Re: Python script for thinkingcleaner roomba interface

there is no trivial way to do this in the script. the beauty of the script is that it is fast and ends once it receives/sends the roomba messages. If we want to add an asynchronous action it can not be in the same method, as it waits for the next call from roomba. so we would need to launch another script that waits for x / y seconds before it queries it again. and then you would need to handle already called scripts if you get the same message twice from roomba.

better to do this in an independent (external script) something like this:
import time
option="B"
while True:
read indigo variable x,y, and check if any change:
- if option A: nextCall=time.time()+30*60; option="A"
- if option B: nextCall=time.time() + 60. ; option ="B"
if time.time() > nextCall:
send msg to roomba
if option=="A": nextCall=time.time() + 30*60
else: nextCall=time.time()+60
time.sleep(5)
if you need help let me know

Karl

Posted on
Sat Feb 13, 2016 2:28 pm
kw123 offline
User avatar
Posts: 8333
Joined: May 12, 2013
Location: Dallas, TX

Re: Python script for thinkingcleaner roomba interface

in python (compiles w/o error):
Code: Select all
import time
nextCall=0.
lastState=""

while True:
   theState= indigo.variables[1925578960].value # this is the state of roomba

   if theState != lastState:
      if theState == 'cleaner_state':  ## you need to put the proper string here
         nextCall=time.time()+30*60
         lastState="cleaning"
      else:
         nextCall=time.time()+60
         lastState="home"

   if time.time() > nextCall:
      continue # put your send msg to roomba here

      if lastState !="home":    # put the next msg time into nextCall: 1 minute or 30 minutes
         nextCall=time.time() + 30*60
      else:
         nextCall=time.time()+60
   time.sleep(5)

Page 1 of 1

Who is online

Users browsing this forum: No registered users and 2 guests