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
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() if mqttc is not None: mqttc.disconnect()