#myroomba.enable_map(enable=True, enableMapWithText=False, roomOutline=False, rssiMap=True, rssiMapGoodSignal=-30, rssiMapBadSignal=-70, auto_rotate=False, mapSize="(1100,1370,-100,-240,0,0)", mapPath="/dataDisk/htdocs/roomba", iconPath="/dataDisk/Roomba980-Python/roomba/res/") #enable live maps, class default is myroomba.enable_map( enable=True, enableMapWithText=False, roomOutline=False, auto_rotate=False, mapSize="(1100,1370,-100,-250,0,0)", mapPath="{{htdocs_path}}roomba", iconPath="/opt/roomba/res/") #enable live maps, class default is no maps if broker is not None: myroomba.set_mqtt_client( mqttc, brokerFeedback ) #if you want to publish Roomba data to your own mqtt broker (default is not to) if you have more than one roomba, and assign a roombaName, it is addded to this topic (ie brokerFeedback/roombaName) #finally connect to Roomba - (required!) myroomba.connect() print("<CMTRL C> to exit") print("Subscribe to /roomba/feedback/# to see published data") try: if mqttc is not None: mqttc.loop_forever() else: while True: #print("Roomba Data: %s" % json.dumps(myroomba.master_state, indent=2)) time.sleep(5) except (KeyboardInterrupt, SystemExit): print("System exit Received - Exiting program") myroomba.disconnect()
def main(): #----------- Local Routines ------------ def create_html(myroomba,mappath="."): ''' Create html files for live display of roomba maps - but only if they don't already exist NOTE add {{ for { in html where you need variable substitution ''' #default css and html css = '''\ body { background-color: white; margin: 0; color: white; padding: 0; } img,video { width: auto; max-height:100%; } ''' html = '''\ <!DOCTYPE html> <html> <head> <link href="style.css" rel="stylesheet" type="text/css"> </head> <script> function refresh(node) {{ var times = 1000; // gap in Milli Seconds; (function startRefresh() {{ var address; if(node.src.indexOf('?')>-1) address = node.src.split('?')[0]; else address = node.src; node.src = address+"?time="+new Date().getTime(); setTimeout(startRefresh,times); }})(); }} window.onload = function() {{ var node = document.getElementById('img'); refresh(node); // you can refresh as many images you want just repeat above steps }} </script> <body> <img id="img" src="{}map.png" alt="Roomba Map Live" style="position:absolute;top:0;left:0"/> </body> </html> '''.format(myroomba.roombaName) def write_file(fname, data, mode=0o666): if not os.path.isfile(fname): log.warn("{} file not found, creating".format(fname)) try: with open(fname , "w") as fn: fn.write(textwrap.dedent(data)) os.chmod(fname, mode) except (IOError, PermissionError) as e: log.error("unable to create file {}, error: {}".format(fname, e)) #check if style.css exists, if not create it css_path = '{}/style.css'.format(mappath) write_file(css_path, css) #check if html exists, if not create it html_path = '{}/{}roomba_map.html'.format(mappath, myroomba.roombaName) write_file(html_path, html, 0o777) def setup_logger(logger_name, log_file, level=logging.DEBUG, console=False): try: l = logging.getLogger(logger_name) formatter = logging.Formatter('[%(asctime)s][%(levelname)5.5s](%(name)-20s) %(message)s') if log_file is not None: fileHandler = logging.handlers.RotatingFileHandler(log_file, mode='a', maxBytes=10000000, backupCount=10) fileHandler.setFormatter(formatter) if console == True: #formatter = logging.Formatter('[%(levelname)1.1s %(name)-20s] %(message)s') streamHandler = logging.StreamHandler() streamHandler.setFormatter(formatter) l.setLevel(level) if log_file is not None: l.addHandler(fileHandler) if console == True: l.addHandler(streamHandler) except Exception as e: print("Error in Logging setup: %s - do you have permission to write the log file??" % e) sys.exit(1) arg = parse_args() #note: all options can be included in the config file if arg.debug: log_level = logging.DEBUG else: log_level = logging.INFO #setup logging setup_logger('Roomba', arg.log, level=log_level,console=arg.echo) #log = logging.basicConfig(level=logging.DEBUG, # format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') log = logging.getLogger('Roomba') log.info("*******************") log.info("* Program Started *") log.info("*******************") log.debug('Debug Mode') log.info("Roomba.py Version: %s" % Roomba.__version__) log.info("Python Version: %s" % sys.version.replace('\n','')) if HAVE_MQTT: import paho.mqtt log.info("Paho MQTT Version: %s" % paho.mqtt.__version__) if HAVE_CV2: log.info("CV Version: %s" % cv2.__version__) if HAVE_PIL: import PIL #bit of a kludge, just to get the version number log.info("PIL Version: %s" % PIL.__version__) log.debug("-- DEBUG Mode ON -") log.info("<CNTRL C> to Exit") log.info("Roomba MQTT data Interface") group = None options = vars(arg) #use args as dict if arg.blid is None or arg.password is None: get_passwd = Password(arg.roomba_ip,file=arg.configfile) roombas = get_passwd.get_roombas() else: roombas = {arg.roomba_ip: {"blid": arg.blid, "password": arg.password, "roomba_name": arg.roomba_name}} roomba_list = [] for addr, info in roombas.items(): log.info("Creating Roomba object {}, {}".format(addr, info.get("roomba_name", addr))) #get options from config (if they exist) this overrides command line options. for opt, value in options.copy().items(): config_value = info.get(opt) if config_value is None: options[opt] = value elif value is None or isinstance(value, str): options[opt] = config_value else: options[opt] = literal_eval(str(config_value)) # minnimum required to connect on Linux Debian system # myroomba = Roomba(address, blid, roombaPassword) myroomba = Roomba(addr, blid=arg.blid, password=arg.password, topic=arg.topic, roombaName=arg.roomba_name, webport=arg.webport) if arg.webport: arg.webport+=1 if arg.exclude: myroomba.exclude = arg.exclude #set various options myroomba.set_options(raw=arg.raw, indent=arg.indent, pretty_print=arg.pretty_print, max_sqft=arg.max_sqft) if arg.mappath and arg.mapsize and arg.drawmap: # auto create html files (if they don't exist) create_html(myroomba, arg.mappath) # enable live maps, class default is no maps myroomba.enable_map(enable=True, mapSize=arg.mapsize, mapPath=arg.mappath, iconPath=arg.iconpath, roomOutline=arg.room_outline, floorplan=arg.floorplan) if arg.broker is not None: # if you want to publish Roomba data to your own mqtt broker # (default is not to) if you have more than one roomba, and # assign a roombaName, it is addded to this topic # (ie brokerFeedback/roombaName) myroomba.setup_mqtt_client(arg.broker, arg.port, arg.user, arg.broker_password, arg.broker_feedback, arg.broker_command, arg.broker_setting) roomba_list.append(myroomba) loop = asyncio.get_event_loop() loop.set_debug(arg.debug) group = asyncio.gather(*[myroomba.async_connect() for myroomba in roomba_list]) if not group: for myroomba in roomba_list: myroomba.connect() #start each roomba connection individually try: loop.run_forever() except (KeyboardInterrupt, SystemExit): log.info("System exit Received - Exiting program") for myroomba in roomba_list: myroomba.disconnect() log.info('Program Exited') finally: pass
class PublishMission: def __init__(self): rospy.init_node("mission_publisher", anonymous=True) config_path = rospy.get_param("~config") try: with open(config_path) as f: content = f.read() config = yaml.load(content, Loader=yaml.FullLoader) rospy.logdebug(f"Configuration dict: {content}") except Exception as e: while True: rospy.logerr("Configuration file is broken or not readable!") rospy.logerr(e) rospy.sleep(5) self.myroomba = Roomba(config["roomba"]["IP"], config["roomba"]["blid"], config["roomba"]["password"]) self.myroomba.connect() time.sleep(5) while not self.myroomba.roomba_connected: #print(1) time.sleep(1) print("Roomba connected") rospy.Service("send_command", Command, self.send_command) self.pub_mission = rospy.Publisher("mission", Mission, queue_size=10) def send_command(self, req): if req.command == "Start": self.myroomba.send_command("start") return ("Starting cleaning") elif req.command == "Stop": self.myroomba.send_command("stop") return ("Stop cleaning") elif req.command == "Home": self.myroomba.send_command("dock") return ("Going back home") else: return ("Wrong command") def get_msg_data(self, data, data_state, data_error): miss_msg = Mission() miss_msg.name = data["name"] miss_msg.batteryPercentage = str(data["batPct"]) miss_msg.mission_cycle = data["cleanMissionStatus"]["cycle"] miss_msg.mission_phase = data["cleanMissionStatus"]["phase"] miss_msg.current_state = data_state miss_msg.error = data_error miss_msg.mission_notReady = str(data["cleanMissionStatus"]["notReady"]) miss_msg.robotPositionXY[0] = data["pose"]["point"]["x"] miss_msg.robotPositionXY[1] = data["pose"]["point"]["y"] miss_msg.robotPositionAngle = data["pose"]["theta"] miss_msg.audio = str(data["audio"]["active"]) return (miss_msg) def spin(self): rate = rospy.Rate(1) while not rospy.is_shutdown(): #data = {"name": "robot", "batPct": 100, "cleanMissionStatus": {"cycle": "none", "phase": "charge", "error": 0, "notReady": 0}, "pose": {"point": {"x": 100, "y": 20},"theta": -28}, "audio": {"active": True}} #data_state = "Charging" data_state = self.myroomba.current_state data1 = self.myroomba.master_state data_error = self.myroomba.error_message data = data1["state"]["reported"] mission_msg = self.get_msg_data(data, data_state, data_error) self.pub_mission.publish(mission_msg) rate.sleep()