import logging import os import time import subprocess debug = "no" # set to 'yes' to print messages to console working_dir = "/opt/slackbot/palo-blacklister" logfile = "%s/blacklist.log" % working_dir BLfile = "%s/ipv4bl.txt" % working_dir logging.basicConfig(filename=logfile,format='%(asctime)s %(message)s',level=logging.INFO) logging.info('Application started. Listening on port: %s',config.listen_port) app = application = bottle.Bottle() @app.route('/', method='POST') def slack_post(): body = bottle.request.body.read() token = bottle.request.forms.get('token') team_id = bottle.request.forms.get('team_id') team_domain = bottle.request.forms.get('team_domain') service_id = bottle.request.forms.get('service_id') channel_id = bottle.request.forms.get('channel_id') channel_name = bottle.request.forms.get('channel_name') timestamp = bottle.request.forms.get('timestamp') user_id = bottle.request.forms.get('user_id') user_name = bottle.request.forms.get('user_name') args = bottle.request.forms.get('text') trigger_words = bottle.request.forms.get('trigger_words')
import bottle import urllib import facebook import crawl import os app_id = '409886029080423' app_secret = '65045404878c33bac7b538f6f6dbeab9' tokens = {} app = bottle.Bottle() app_access_token = facebook.get_app_access_token(app_id, app_secret) @app.route('/') def home(): bottle.redirect('/login') @app.route('/login') def login(): args = dict(client_id=app_id, redirect_uri='http://*****:*****@app.route('/callback') def callback(): if bottle.request.query.error: return '''%s\n<a href="/login">Click here to retry.</a>'''%bottle.request.query.error_description args = dict(client_id=app_id, redirect_uri='http://localhost:8080/callback') args['client_secret'] = app_secret
# 51 Franklin Street, Fifth Floor, # Boston, MA 02110-1301 USA # import json import rest_fruid import rest_server import rest_sensors import rest_bmc import rest_gpios import rest_modbus import rest_slotid import rest_psu_update import rest_fcpresent import bottle commonApp = bottle.Bottle() # Handler for root resource endpoint @commonApp.route('/api') def rest_api(): result = { "Information": { "Description": "Wedge RESTful API Entry", }, "Actions": [], "Resources": ["sys"], } return result
def __init__(self, robot, host='0.0.0.0', port='6969', quiet=True): AbstractServer.__init__(self, robot, host, port) self.quiet = quiet self.app = bottle.Bottle() self.app.install(EnableCors()) rr = self.restfull_robot # Copy Snap files from system directory to user directory. It avoids # right issue while PyPot is installed from pip in an admin directory snap_system_projects_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'snap_projects') xml_files = [os.path.join(snap_system_projects_directory, f) for f in os.listdir(snap_system_projects_directory) if f.endswith('.xml')] for xml_file in xml_files: dst = os.path.join(get_snap_user_projects_directory(), os.path.basename(xml_file)) logger.info('Copy snap project from {}, to {}'.format(xml_file, dst)) shutil.copyfile(xml_file, dst) set_snap_server_variables(find_local_ip(), port, path=get_snap_user_projects_directory()) @self.app.get('/') def get_sitemap(): return '</br>'.join([cgi.escape(r.rule.format()) for r in self.app.routes]) @self.app.get('/motors/<alias>') def get_motors(alias): return '/'.join(rr.get_motors_list(alias)) @self.app.get('/motor/<motor>/get/<register>') def get_motor_register(motor, register): return str(rr.get_motor_register_value(motor, register)) @self.app.get('/motors/get/positions') def get_motors_positions(): get_pos = lambda m: rr.get_motor_register_value( m, 'present_position') msg = '/'.join('{}'.format(get_pos(m)) for m in rr.get_motors_list()) msg = ';'.join('{}'.format(get_pos(m)) for m in rr.get_motors_list()) return msg @self.app.get('/motors/alias') def get_robot_aliases(): return '/'.join('{}'.format(alias) for alias in rr.get_motors_alias()) @self.app.get('/motors/<motors>/get/<register>') def get_motors_registers(motors,register): """ Allow getting of motors register with a single http request Be carefull: with lot of motors, it could overlap the GET max lentgh of your web browser """ motors = motors.split(';') return ';'.join(str(rr.get_register_value(m, register)) for m in motors) @self.app.get('/motors/set/goto/<motors_position_duration>') def set_motors_goto(motors_position_duration): """ Allow lot of motors position settings with a single http request Be carefull: with lot of motors, it could overlap the GET max lentgh of your web browser """ for m_settings in motors_position_duration.split(';'): settings = m_settings.split(':') rr.set_goto_position_for_motor(settings[0], float(settings[1]), float(settings[2])) return 'Done!' @self.app.get('/motors/set/registers/<motors_register_value>') def set_motors_registers(motors_register_value): """ Allow lot of motors register settings with a single http request Be carefull: with lot of motors, it could overlap the GET max lentgh of your web browser """ for m_settings in motors_register_value.split(';'): settings = m_settings.split(':') rr.set_motor_register_value(settings[0], settings[1], make_tuple(settings[2])) return 'Done!' # TODO : delete ? @self.app.get('/motors/set/positions/<positions>') def set_motors_positions(positions): positions = map(lambda s: float(s), positions[:-1].split(';')) for m, p in zip(rr.get_motors_list(), positions): rr.set_motor_register_value(m, 'goal_position', p) return 'Done!' @self.app.get('/motor/<motor>/set/<register>/<value>') def set_reg(motor, register, value): rr.set_motor_register_value(motor, register, float(value)) return 'Done!' @self.app.get('/motor/<motor>/goto/<position>/<duration>') def set_goto(motor, position, duration): rr.set_goto_position_for_motor( motor, float(position), float(duration)) return 'Done!' @self.app.get('/snap-blocks.xml') def get_pypot_snap_blocks(): with open(os.path.join(get_snap_user_projects_directory(), 'pypot-snap-blocks.xml')) as f: return f.read() @self.app.get('/snap/<project>') def get_snap_projects(project): with open(os.path.join(get_snap_user_projects_directory(), '{}.xml'.format(project))) as f: return f.read() @self.app.get('/ip') def get_ip(): return socket.gethostbyname(socket.gethostname()) @self.app.get('/reset-simulation') def reset_simulation(): if hasattr(robot, 'reset_simulation'): robot.reset_simulation() return 'Done!' @self.app.get('/primitives') def get_primitives(): return '/'.join(rr.get_primitives_list()) @self.app.get('/primitives/running') def get_running_primitives(): return '/'.join(rr.get_running_primitives_list()) @self.app.get('/primitive/<primitive>/start') def start_primitive(primitive): rr.start_primitive(primitive) return 'Done!' @self.app.get('/primitive/<primitive>/stop') def stop_primitive(primitive): rr.stop_primitive(primitive) return 'Done!' @self.app.get('/primitive/<primitive>/pause') def pause_primitive(primitive): rr.pause_primitive(primitive) return 'Done!' @self.app.get('/primitive/<primitive>/resume') def resume_primitive(primitive): rr.resume_primitive(primitive) return 'Done!' @self.app.get('/primitive/<primitive>/properties') def get_primitive_properties_list(primitive): return '/'.join(rr.get_primitive_properties_list(primitive)) @self.app.get('/primitive/<primitive>/get/<property>') def get_primitive_property(primitive, property): return rr.get_primitive_property(primitive, property) @self.app.get('/primitive/<primitive>/set/<property>/<value>') def set_primitive_property(primitive, property, value): return rr.set_primitive_property(primitive, property, value) @self.app.get('/primitive/<primitive>/methodes') def get_primitive_methodes_list(primitive): return '/'.join(rr.get_primitive_methods_list(primitive)) @self.app.get('/primitive/<primitive>/call/<method>/<args>') def call_primitive_methode(primitive, method, args): kwargs = dict(item.split(":") for item in args.split(";")) return rr._call_primitive_method(primitive, method, **kwargs) # Hacks (no restfull) to record movements @self.app.get('/primitive/MoveRecorder/<move_name>/start') def start_move_recorder(move_name): rr.start_move_recorder(move_name) return 'Done!' @self.app.get('/primitive/MoveRecorder/<move_name>/stop') def stop_move_recorder(move_name): rr.stop_move_recorder(move_name) return 'Done!' @self.app.get('/primitive/MoveRecorder/<move_name>/attach/<motors>') def attach_move_recorder(move_name, motors): rr.attach_move_recorder(move_name, motors.split(';')) return 'Done!' @self.app.get('/primitive/MoveRecorder/<move_name>/get_motors') def get_move_recorder_motors(move_name): motors = rr.get_move_recorder_motors(move_name) return '/'.join(motors) if motors is not None else 'None' @self.app.get('/primitive/MoveRecorder/<move_name>/start/<motors>') def start_move_recorder_with_motors(move_name, motors): # raise DeprecationWarning rr.start_move_recorder(move_name, motors.split(';')) return 'Done!' @self.app.get('/primitive/MoveRecorder/<move_name>/remove') def remove_move_record(move_name): rr.remove_move_record(move_name) return 'Done!' @self.app.get('/primitive/MoveRecorder') def get_available_records(): return '/'.join(rr.get_available_record_list()) @self.app.get('/primitive/MovePlayer') def get_available_records2(): return '/'.join(rr.get_available_record_list()) @self.app.get('/primitive/MovePlayer/<move_name>/start') def start_move_player(move_name): return str(rr.start_move_player(move_name)) @self.app.get('/primitive/MovePlayer/<move_name>/start/<move_speed>') def start_move_player_with_speed(move_name, move_speed): return str(rr.start_move_player(move_name, float(move_speed))) @self.app.get('/primitive/MovePlayer/<move_name>/start/<move_speed>/backwards') def start_move_player_backwards_with_speed(move_name, move_speed): return str(rr.start_move_player(move_name, float(move_speed), backwards=True)) @self.app.get('/primitive/MovePlayer/<move_name>/stop') def stop_move_player(move_name): rr.stop_primitive('_{}_player'.format(move_name)) return 'Done!' @self.app.get('/detect/<marker>') def detect_marker(marker): markers = { 'tetris': [112259237], 'caribou': [221052793], 'lapin': [44616414], } detected = rr.robot.marker_detector.markers return str(any([m.id in markers[marker] for m in detected]))
def main(): global SESSION_HANDLER parser = argparse.ArgumentParser(description="AI for Earth Land Cover") parser.add_argument("-v", "--verbose", action="store_true", help="Enable verbose debugging", default=False) parser.add_argument("--host", action="store", dest="host", type=str, help="Host to bind to", default="0.0.0.0") parser.add_argument("--port", action="store", dest="port", type=int, help="Port to listen on", default=8080) parser.add_argument( "--disable_checkpoints", action="store_true", help="Disables the ability to save checkpoints on the server") args = parser.parse_args(sys.argv[1:]) # Create session factory to handle incoming requests SESSION_HANDLER = SessionHandler(args) SESSION_HANDLER.start_monitor(SESSION_TIMEOUT_SECONDS) # Setup logging log_path = os.path.join(os.getcwd(), "tmp/logs/") setup_logging(log_path, "server") # Make sure some directories exist os.makedirs("tmp/checkpoints/", exist_ok=True) os.makedirs("tmp/downloads/", exist_ok=True) os.makedirs("tmp/logs/", exist_ok=True) os.makedirs("tmp/output/", exist_ok=True) # TODO: Remove this after we rework os.makedirs("tmp/session/", exist_ok=True) # Setup the bottle server app = bottle.Bottle() app.add_hook("after_request", enable_cors) app.add_hook( "before_request", manage_sessions ) # before every request we want to check to make sure there are no session issues # API paths app.route( "/predPatch", method="OPTIONS", callback=do_options ) # TODO: all of our web requests from index.html fire an OPTIONS call because of https://stackoverflow.com/questions/1256593/why-am-i-getting-an-options-request-instead-of-a-get-request, we should fix this app.route('/predPatch', method="POST", callback=pred_patch) app.route("/predTile", method="OPTIONS", callback=do_options) app.route('/predTile', method="POST", callback=pred_tile) app.route("/downloadAll", method="OPTIONS", callback=do_options) app.route('/downloadAll', method="POST", callback=download_all) app.route("/getInput", method="OPTIONS", callback=do_options) app.route('/getInput', method="POST", callback=get_input) app.route("/recordCorrection", method="OPTIONS", callback=do_options) app.route('/recordCorrection', method="POST", callback=record_correction) app.route("/retrainModel", method="OPTIONS", callback=do_options) app.route('/retrainModel', method="POST", callback=retrain_model) app.route("/resetModel", method="OPTIONS", callback=do_options) app.route('/resetModel', method="POST", callback=reset_model) app.route("/doUndo", method="OPTIONS", callback=do_options) app.route("/doUndo", method="POST", callback=do_undo) app.route("/createSession", method="OPTIONS", callback=do_options) app.route("/createSession", method="POST", callback=create_session) app.route("/killSession", method="OPTIONS", callback=do_options) app.route("/killSession", method="POST", callback=kill_session) app.route("/getSessionStatus", method="OPTIONS", callback=do_options) app.route("/getSessionStatus", method="POST", callback=get_session_status) # Checkpoints app.route("/createCheckpoint", method="OPTIONS", callback=do_options) app.route("/createCheckpoint", method="POST", callback=checkpoint_wrapper(args.disable_checkpoints)) app.route("/getCheckpoints", method="GET", callback=get_checkpoints) # Sessions app.route("/whoami", method="GET", callback=whoami) # Content paths app.route("/", method="GET", callback=get_landing_page) app.route("/data/basemaps/<filepath:re:.*>", method="GET", callback=get_basemap_data) app.route("/data/zones/<filepath:re:.*>", method="GET", callback=get_zone_data) app.route("/tmp/downloads/<filepath:re:.*>", method="GET", callback=get_downloads) app.route("/favicon.ico", method="GET", callback=get_favicon) app.route("/<filepath:re:.*>", method="GET", callback=get_everything_else) manage_session_folders() session_opts = { 'session.type': 'file', #'session.cookie_expires': 3000, # session cookie 'session.data_dir': SESSION_FOLDER, 'session.auto': True } app = beaker.middleware.SessionMiddleware(app, session_opts) server = cheroot.wsgi.Server((args.host, args.port), app) server.max_request_header_size = 2**13 server.max_request_body_size = 2**27 LOGGER.info("Server initialized") try: server.start() finally: server.stop()
def setUp(self): self.engine = create_engine('sqlite:///:memory:') self.app = bottle.Bottle(catchall=False)
def start(port=0): multiprocessing.set_start_method('spawn') bottle.debug(True) bottle_app = bottle.Bottle() logger = create_logger('geventwebsocket.logging') logger.setLevel(logging.DEBUG) logger.addHandler(logging.StreamHandler()) logger.propagate = False if app.app_archive: zf = ZipFile(app.app_archive, 'r') print(zf.namelist()) print(zf.getinfo('webgui2/dist/index.html')) def serve_file(path): try: print('serving', path, 'from', 'webgui2/dist/' + path) info = zf.getinfo('webgui2/dist/' + path) if info.is_dir(): return bottle.HTTPError(403) size = info.file_size bottle.response.content_length = size bottle.response.content_type = bottle.mimetypes.guess_type(path)[0] return zf.open(info, 'r') except KeyError: return bottle.HTTPError(404) except: import traceback traceback.print_exc() else: root = Path(__file__).parent.joinpath('dist') def serve_file(path): return bottle.static_file(path, root) httpsock = gevent.socket.socket(gevent.socket.AF_INET, gevent.socket.SOCK_STREAM) httpsock.bind(('127.0.0.1', port)) httpsock.listen() token = '1145141919' @bottle_app.route("/") def serve_root(): return serve_file("index.html") @bottle_app.route('/itemimg/<name>.png') def itemimg(name): logger.info('serving file %s', name) import imgreco.itemdb imgreco.itemdb.update_extra_items() items = imgreco.itemdb.all_known_items itemres = items.get(name, None) if itemres: bottle.response.content_type = 'image/png' return itemres.open() else: return 404 def readws(ws): while True: try: msg = ws.receive() except WebSocketError: return None if msg is not None: if not isinstance(msg, str): continue try: obj = json.loads(msg) except: logger.error("invalid JSON") continue logger.debug("received request %r", obj) return obj else: return None @bottle_app.route("/ws") def rpc_endpoint(): wsock : geventwebsocket.websocket.WebSocket = bottle.request.environ.get('wsgi.websocket') if not wsock: bottle.abort(400, 'Expected WebSocket request.') authorized = False wsock.send('{"type":"need-authorize"}') while True: try: obj = readws(wsock) if obj is None: break request_type = obj.get('type', None) if request_type == 'web:authorize': client_token = obj.get('token', None) if client_token == token: authorized = True break except WebSocketError: break if authorized: logger.info('client authorized') from .worker_launcher import worker_process inq = multiprocessing.Queue() outq = multiprocessing.Queue() p = multiprocessing.Process(target=worker_process, args=(inq, outq), daemon=True) logger.info('spawning worker process') p.start() pool : gevent.threadpool.ThreadPool = gevent.get_hub().threadpool error = False logger.info('starting worker loop') outqread = pool.spawn(outq.get) wsread = gevent.spawn(readws, wsock) while not error: for task in gevent.wait((outqread, wsread), count=1): if task is outqread: try: outval = outqread.get() except: logger.error('read worker output failed with exception', exc_info=True) error = True break gevent.spawn(wsock.send, json.dumps(outval)) outqread = pool.spawn(outq.get) elif task is wsread: try: obj = wsread.get() except: logger.error('read message from websocket failed with exception', exc_info=True) error = True break if obj is None: error = True break wsread = gevent.spawn(readws, wsock) pool.spawn(inq.put, obj) logger.info('worker loop stopped') with contextlib.suppress(Exception): gevent.kill(wsread) wsock.close() inq.put_nowait(None) p.kill() @bottle_app.route("/<filepath:path>") def serve_static(filepath): return serve_file(filepath) group = gevent.pool.Pool() server = gevent.pywsgi.WSGIServer(httpsock, bottle_app, handler_class=WebSocketHandler, log=logger, spawn=group) url = f'http://{server.address[0]}:{server.address[1]}/?token={token}' print(url) server_task = gevent.spawn(server.serve_forever) if port != 0: server_task.get() return from .webhost import get_host host = get_host() host.start(url, 1080, 820) if host.wait_handle: # neither gevent nor pywebview like non-main thread webview_task = gevent.get_hub().threadpool.spawn(host.wait_handle) webview_task.wait() else: idlechk_interval = getattr(host, 'poll_interval', 60) idlecount = 1 while True: gevent.sleep(idlechk_interval) if len(group) == 0: idlecount += 1 else: idlecount = 0 if idlecount >= 3: print("stopping idle server") break # gevent.util.print_run_info() server.stop()
def __init__(self): self._server = None self._server_thr = None self.app = bottle.Bottle()
from zeroconf import ServiceInfo, Zeroconf from ethoscope.web_utils.control_thread import ControlThread from ethoscope.web_utils.helpers import * from ethoscope.web_utils.record import ControlThreadVideoRecording #from bottle import Bottle, ServerAdapter, request, server_names try: from cheroot.wsgi import Server as WSGIServer except ImportError: from cherrypy.wsgiserver import CherryPyWSGIServer as WSGIServer api = bottle.Bottle() tracking_json_data = {} recording_json_data = {} update_machine_json_data = {} ETHOSCOPE_DIR = None def list_options(category): """ Return a list of str with the names of the classes that can be passed for a given category. """ return [cls.__name__ for cls in ControlThread._option_dict[category]['possible_classes']] class WrongMachineID(Exception):
import urllib.request import bottle from bottle import request, response from . import MANAGEMENT_DAEMON_URL, ROLES from . import (COLLECTOR_STATE_CONFIGURED, COLLECTOR_STATE_FAILURE, COLLECTOR_STATE_NOT_INSTALLED, COLLECTOR_STATE_INSTALLED, COLLECTOR_STATE_PARTIAL_FAILURE) from .config import CONFIG FILE_UPLOAD_PATH = CONFIG.get('file_upload_path') APP = bottle.Bottle() # Default value for "Expires" header (don't allow to cache responses) EXPIRES_DEFAULT = datetime.datetime(1970, 1, 1) def abort(code=500, text='Unknown Error.'): """Aborts execution and causes a HTTP error.""" body = json.dumps(dict(message=text)) raise bottle.HTTPResponse(body, code, headers={'content_type': 'application/json'}) @APP.route('/context.json') def context(): """Output context data.""" # read user information from Apache environment
def start_adminpanel(zigate_instance, host=ADMINPANEL_HOST, port=ADMINPANEL_PORT, mount=None, prefix=None, autostart=True, daemon=True, quiet=True, debug=False): ''' mount: url prefix used to mount bottle application prefix: special prefix added when using get_url in template, eg proxy.php ''' app = bottle.Bottle() app.install( bottle.JSONPlugin(json_dumps=lambda s: dumps(s, cls=DeviceEncoder))) def get_url(routename, **kwargs): ''' customized get_url to allow additional prefix args ''' redirect = kwargs.pop('redirect', False) scriptname = bottle.request.environ.get('SCRIPT_NAME', '').strip('/') + '/' location = app.router.build(routename, **kwargs).lstrip('/') url = bottle.urljoin(bottle.urljoin('/', scriptname), location) if prefix and not redirect: url = prefix + '?' + bottle.urlencode({'q': url}) append = '?' if '?' in url: append = '&' url += '{}_={}'.format(append, time.time()) return url def redirect(routename, **kwargs): ''' convenient function to redirect using routename instead of url ''' return bottle.redirect(get_url(routename, redirect=True, **kwargs)) bottle.BaseTemplate.defaults['get_url'] = get_url bottle.BaseTemplate.defaults['zigate'] = zigate_instance app.zigate = zigate_instance @app.route('/', name='index') @bottle.view('index') def index(): connected = zigate_instance.connection and zigate_instance.connection.is_connected( ) grouped_devices = {} processed = [] def add_device_to_group(group, addr, endpoint=''): name = 'Missing' last_seen = '' if addr == zigate_instance.addr: name = 'ZiGate' zdev = zigate_instance.get_device_from_addr(addr) if zdev: name = str(zdev) last_seen = zdev.info.get('last_seen', '') else: name = '{} ({})'.format(name, addr) group.append({ 'addr': addr, 'endpoint': endpoint, 'name': name, 'last_seen': last_seen }) for group, group_devices in zigate_instance.groups.items(): grouped_devices[group] = [] for device in group_devices: addr = device[0] endpoint = device[1] processed.append(addr) add_device_to_group(grouped_devices[group], addr, endpoint) grouped_devices[''] = [] for device in zigate_instance.devices: if device.addr not in processed: add_device_to_group(grouped_devices[''], device.addr) port = zigate_instance._port or 'auto' if hasattr(zigate_instance, '_host'): port = '{}:{}'.format(zigate_instance._host, port) return { 'libversion': zigate_version.__version__, 'port': port, 'connected': connected, 'version': zigate_instance.get_version_text(), 'model': zigate_instance.model, 'groups': zigate_instance.groups, 'grouped_devices': grouped_devices, } @app.route('/networkmap', name='networkmap') @bottle.view('networkmap') def networkmap(): return @app.route('/device/<addr>', name='device') @bottle.view('device') def device(addr): device = zigate_instance.get_device_from_addr(addr) if not device: return redirect('index') return {'device': device} @app.route('/raw_command', name='raw_command', method=['POST']) def raw_command(): cmd = bottle.request.forms.get('cmd') data = bottle.request.forms.get('data') cmd = int(cmd, 16) zigate_instance.send_data(cmd, data) return redirect('index') @app.route('/api/permit_join', name='api_permit_join') def permit_join(): zigate_instance.permit_join() return redirect('index') @app.route('/api/reset', name='api_reset') def reset(): zigate_instance.reset() return redirect('index') @app.route('/api/led', name='api_led') def set_led(): on = bottle.request.query.get('on', 'true') == 'true' zigate_instance.set_led(on) return redirect('index') @app.route('/api/discover/<addr>', name='api_discover') def api_discover(addr): zigate_instance.discover_device(addr, True) return redirect('device', addr=addr) @app.route('/api/refresh/<addr>', name='api_refresh') def api_refresh(addr): zigate_instance.refresh_device(addr) return redirect('device', addr=addr) @app.route('/api/remove/<addr>', name='api_remove') def api_remove(addr): force = bottle.request.query.get('force', 'false') == 'true' zigate_instance.remove_device(addr, force) return redirect('index') @app.route('/device/<addr>/save', name='device_save', method=['GET', 'POST']) def device_save(addr): device = zigate_instance.get_device_from_addr(addr) if not device: return redirect('index') device.name = bottle.request.forms.name return redirect('device', addr=addr) @app.route('/api/devices', name='api_devices') def devices(): devices = [{ 'info': { 'addr': zigate_instance.addr, 'ieee': zigate_instance.ieee }, 'friendly_name': 'ZiGate' }] for d in zigate_instance.devices: device = d.to_json() device['friendly_name'] = str(d) devices.append(device) return {'devices': devices} @app.route('/api/network_table', name='api_network_table') def network_table(): force = bottle.request.query.get('force', 'false') == 'true' return {'network_table': zigate_instance.build_neighbours_table(force)} kwargs = {'host': host, 'port': port, 'quiet': quiet, 'debug': debug} if autostart: r_app = app if mount: root_app = bottle.Bottle() root_app.mount(mount, app) r_app = root_app if daemon: t = threading.Thread(target=r_app.run, kwargs=kwargs, daemon=True) t.start() else: r_app.run(**kwargs) return app