'''Web interface for the Raspberry Pi Information Screen. by elParaguayo This module defines the underlying API. Screens can define their own web pages for custom configuration by using methods available in this API. API format: [HOST]/api/<screenname>/configure GET: returns JSON format of user-configurable settings for screen POST: takes JSON format of updated configuration. [HOST]/api/<screenname>/enable GET: enable the selected screen [HOST]/api/<screenname>/disable GET: disable the selected screen [HOST]/api/<screenname>/view GET: change to screen API Response format: successful: {"status": "success", "data": [body of response]} unsuccessful: {"status": "error", "message": [Error message]} ''' from threading import Thread from time import sleep import os import json import imp from kivy.app import App from bottle import Bottle, template, request, response from getplugins import getPlugins class InfoScreenAPI(Bottle): def __init__(self, infoscreen, folder): super(InfoScreenAPI, self).__init__() # Get reference to base screen object so API server can # access methods self.infoscreen = infoscreen.base # Get the folder path so we can access config files self.folder = folder # Get the list of screens #self.process_plugins() # Define our routes self.route("/", callback=self.default) self.error_handler[404] = self.unknown # API METHODS self.route("/api/<screen>/configure", callback=self.get_config, method="GET") self.route("/api/<screen>/configure", callback=self.set_config, method="POST") self.route("/api/<screen>/enable", callback=self.enable_screen) self.route("/api/<screen>/disable", callback=self.disable_screen) self.route("/api/<screen>/view", callback=self.view) def api_success(self, data): """Base method for response to successful API calls.""" return {"status": "success", "data": data} def api_error(self, message): """Base method for response to unsuccessful API calls.""" return {"status": "error", "message": message} def get_config(self, screen): """Method to retrieve config file for screen.""" # Define the path to the config file conffile = os.path.join(self.folder, "screens", screen, "conf.json") if os.path.isfile(conffile): # Get the config file with open(conffile, "r") as cfg_file: # Load the JSON object conf = json.load(cfg_file) # Return the "params" section result = self.api_success(conf.get("params", dict())) else: # Something's gone wrong result = self.api_error("No screen called: {}".format(screen)) # Provide the response return json.dumps(result) def set_config(self, screen): try: # Get JSON data js = request.json if js is None: # No data, so provide error return self.api_error("No JSON data received. " "Check headers are set correctly.") else: # Try to save the new config success = self.save_config(screen, js) # If successfully saved... if success: # Reload the screen with the new config self.infoscreen.reload_screen(screen) # Provide success notification return self.api_success(json.dumps(js)) else: # We couldn't save new config return self.api_error("Unable to save configuration.") except: # Something's gone wrong return self.api_error("Invalid data received.") def default(self): # Generic response for unknown requests result = self.api_error("Invalid method.") return json.dumps(result) def unknown(self, addr): return self.default() def view(self, screen): try: self.infoscreen.switch_to(screen) return self.api_success("Changed screen to: {}".format(screen)) except: return self.api_error("Could not change screen.") # Helper Methods ########################################################### def save_config(self, screen, params): try: conffile = os.path.join(self.folder, "screens", screen, "conf.json") conf = json.load(open(conffile, "r")) conf["params"] = params with open(conffile, "w") as config: json.dump(conf, config, indent=4) return True except: return False def enable_screen(self, screen): try: # Update status in config self.change_screen_state(screen, True) # Make sure the screen is added self.infoscreen.add_screen(screen) # Success! return self.api_success("{} screen enabled.".format(screen)) except: # Something went wrong return self.api_error("Could not enable {} screen.".format(screen)) def disable_screen(self, screen): try: # Update status in config self.change_screen_state(screen, False) # Make sure the screen is added self.infoscreen.remove_screen(screen) # Success! return self.api_success("{} screen disabled.".format(screen)) except: # Something went wrong! return self.api_error("Could not disable {} screen.".format(screen)) def change_screen_state(self, screen, enabled): # Build path to config conffile = os.path.join(self.folder, "screens", screen, "conf.json") # Load existing config with open(conffile, "r") as f_config: conf = json.load(f_config) # Change status to desired state conf["enabled"] = enabled # Save the updated config with open(conffile, "w") as f_config: json.dump(conf, f_config, indent=4)