diff --git a/.gitignore b/.gitignore index 35300ae..eeabbad 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ thumbs.db #storage/profiles #config.py .idea/* +.DS_Store diff --git a/kiln-controller.py b/kiln-controller.py index 17fc3f9..35ea742 100755 --- a/kiln-controller.py +++ b/kiln-controller.py @@ -8,7 +8,8 @@ import json import bottle import gevent import geventwebsocket -#from bottle import post, get + +# from bottle import post, get from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler from geventwebsocket import WebSocketError @@ -16,10 +17,11 @@ from geventwebsocket import WebSocketError try: sys.dont_write_bytecode = True import config + sys.dont_write_bytecode = False except: - print ("Could not import config file.") - print ("Copy config.py.EXAMPLE to config.py and adapt it for your setup.") + print("Could not import config file.") + print("Copy config.py.EXAMPLE to config.py and adapt it for your setup.") exit(1) logging.basicConfig(level=config.log_level, format=config.log_format) @@ -27,7 +29,7 @@ log = logging.getLogger("kiln-controller") log.info("Starting kiln controller") script_dir = os.path.dirname(os.path.realpath(__file__)) -sys.path.insert(0, script_dir + '/lib/') +sys.path.insert(0, script_dir + "/lib/") profile_path = config.kiln_profiles_directory from oven import SimulatedOven, RealOven, Profile @@ -45,93 +47,99 @@ ovenWatcher = OvenWatcher(oven) # this ovenwatcher is used in the oven class for restarts oven.set_ovenwatcher(ovenWatcher) -@app.route('/') -def index(): - return bottle.redirect('/picoreflow/index.html') -@app.get('/api/stats') +@app.route("/") +def index(): + return bottle.redirect("/picoreflow/index.html") + + +@app.get("/api/stats") def handle_api(): log.info("/api/stats command received") - if hasattr(oven,'pid'): - if hasattr(oven.pid,'pidstats'): + if hasattr(oven, "pid"): + if hasattr(oven.pid, "pidstats"): return json.dumps(oven.pid.pidstats) -@app.post('/api') +@app.post("/api") def handle_api(): log.info("/api is alive") - # run a kiln schedule - if bottle.request.json['cmd'] == 'run': - wanted = bottle.request.json['profile'] - log.info('api requested run of profile = %s' % wanted) + if bottle.request.json["cmd"] == "run": + wanted = bottle.request.json["profile"] + log.info("api requested run of profile = %s" % wanted) # start at a specific minute in the schedule # for restarting and skipping over early parts of a schedule - startat = 0; - if 'startat' in bottle.request.json: - startat = bottle.request.json['startat'] + startat = 0 + if "startat" in bottle.request.json: + startat = bottle.request.json["startat"] # get the wanted profile/kiln schedule profile = find_profile(wanted) if profile is None: - return { "success" : False, "error" : "profile %s not found" % wanted } + return {"success": False, "error": "profile %s not found" % wanted} # FIXME juggling of json should happen in the Profile class profile_json = json.dumps(profile) profile = Profile(profile_json) - oven.run_profile(profile,startat=startat) + oven.run_profile(profile, startat=startat) ovenWatcher.record(profile) - if bottle.request.json['cmd'] == 'stop': + if bottle.request.json["cmd"] == "stop": log.info("api stop command received") oven.abort_run() - if bottle.request.json['cmd'] == 'memo': + if bottle.request.json["cmd"] == "memo": log.info("api memo command received") - memo = bottle.request.json['memo'] + memo = bottle.request.json["memo"] log.info("memo=%s" % (memo)) # get stats during a run - if bottle.request.json['cmd'] == 'stats': + if bottle.request.json["cmd"] == "stats": log.info("api stats command received") - if hasattr(oven,'pid'): - if hasattr(oven.pid,'pidstats'): + if hasattr(oven, "pid"): + if hasattr(oven.pid, "pidstats"): return json.dumps(oven.pid.pidstats) - return { "success" : True } + return {"success": True} + def find_profile(wanted): - ''' + """ given a wanted profile name, find it and return the parsed json profile object or None. - ''' - #load all profiles from disk + """ + # load all profiles from disk profiles = get_profiles() json_profiles = json.loads(profiles) # find the wanted profile for profile in json_profiles: - if profile['name'] == wanted: + if profile["name"] == wanted: return profile return None -@app.route('/picoreflow/:filename#.*#') + +@app.route("/picoreflow/:filename#.*#") def send_static(filename): log.debug("serving %s" % filename) - return bottle.static_file(filename, root=os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "public")) + return bottle.static_file( + filename, + root=os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), "public"), + ) def get_websocket_from_request(): env = bottle.request.environ - wsock = env.get('wsgi.websocket') + wsock = env.get("wsgi.websocket") if not wsock: - abort(400, 'Expected WebSocket request.') + abort(400, "Expected WebSocket request.") return wsock -@app.route('/control') +@app.route("/control") def handle_control(): wsock = get_websocket_from_request() log.info("websocket (control) opened") @@ -143,7 +151,7 @@ def handle_control(): msgdict = json.loads(message) if msgdict.get("cmd") == "RUN": log.info("RUN command received") - profile_obj = msgdict.get('profile') + profile_obj = msgdict.get("profile") if profile_obj: profile_json = json.dumps(profile_obj) profile = Profile(profile_json) @@ -151,15 +159,15 @@ def handle_control(): ovenWatcher.record(profile) elif msgdict.get("cmd") == "SIMULATE": log.info("SIMULATE command received") - #profile_obj = msgdict.get('profile') - #if profile_obj: + # profile_obj = msgdict.get('profile') + # if profile_obj: # profile_json = json.dumps(profile_obj) # profile = Profile(profile_json) - #simulated_oven = Oven(simulate=True, time_step=0.05) - #simulation_watcher = OvenWatcher(simulated_oven) - #simulation_watcher.add_observer(wsock) - #simulated_oven.run_profile(profile) - #simulation_watcher.record(profile) + # simulated_oven = Oven(simulate=True, time_step=0.05) + # simulation_watcher = OvenWatcher(simulated_oven) + # simulation_watcher.add_observer(wsock) + # simulated_oven.run_profile(profile) + # simulation_watcher.record(profile) elif msgdict.get("cmd") == "STOP": log.info("Stop command received") oven.abort_run() @@ -169,7 +177,7 @@ def handle_control(): log.info("websocket (control) closed") -@app.route('/storage') +@app.route("/storage") def handle_storage(): wsock = get_websocket_from_request() log.info("websocket (storage) opened") @@ -190,18 +198,18 @@ def handle_storage(): wsock.send(get_profiles()) elif msgdict.get("cmd") == "DELETE": log.info("DELETE command received") - profile_obj = msgdict.get('profile') + profile_obj = msgdict.get("profile") if delete_profile(profile_obj): - msgdict["resp"] = "OK" + msgdict["resp"] = "OK" wsock.send(json.dumps(msgdict)) - #wsock.send(get_profiles()) + # wsock.send(get_profiles()) elif msgdict.get("cmd") == "PUT": log.info("PUT command received") - profile_obj = msgdict.get('profile') - #force = msgdict.get('force', False) + profile_obj = msgdict.get("profile") + # force = msgdict.get('force', False) force = True if profile_obj: - #del msgdict["cmd"] + # del msgdict["cmd"] if save_profile(profile_obj, force): msgdict["resp"] = "OK" else: @@ -215,7 +223,7 @@ def handle_storage(): log.info("websocket (storage) closed") -@app.route('/config') +@app.route("/config") def handle_config(): wsock = get_websocket_from_request() log.info("websocket (config) opened") @@ -228,7 +236,7 @@ def handle_config(): log.info("websocket (config) closed") -@app.route('/status') +@app.route("/status") def handle_status(): wsock = get_websocket_from_request() ovenWatcher.add_observer(wsock) @@ -245,31 +253,39 @@ def handle_status(): def get_profiles(): try: profile_files = os.listdir(profile_path) + profile_files.sort() except: profile_files = [] profiles = [] for filename in profile_files: - with open(os.path.join(profile_path, filename), 'r') as f: - profiles.append(json.load(f)) + if filename.startswith("._"): + pass + else: + if filename.endswith(".json"): + with open(os.path.join(profile_path, filename), "r") as f: + profiles.append(json.load(f)) + else: + pass return json.dumps(profiles) def save_profile(profile, force=False): profile_json = json.dumps(profile) - filename = profile['name']+".json" + filename = profile["name"] + ".json" filepath = os.path.join(profile_path, filename) if not force and os.path.exists(filepath): log.error("Could not write, %s already exists" % filepath) return False - with open(filepath, 'w+') as f: + with open(filepath, "w+") as f: f.write(profile_json) f.close() log.info("Wrote %s" % filepath) return True + def delete_profile(profile): profile_json = json.dumps(profile) - filename = profile['name']+".json" + filename = profile["name"] + ".json" filepath = os.path.join(profile_path, filename) os.remove(filepath) log.info("Deleted %s" % filepath) @@ -277,11 +293,15 @@ def delete_profile(profile): def get_config(): - return json.dumps({"temp_scale": config.temp_scale, - "time_scale_slope": config.time_scale_slope, - "time_scale_profile": config.time_scale_profile, - "kwh_rate": config.kwh_rate, - "currency_type": config.currency_type}) + return json.dumps( + { + "temp_scale": config.temp_scale, + "time_scale_slope": config.time_scale_slope, + "time_scale_profile": config.time_scale_profile, + "kwh_rate": config.kwh_rate, + "currency_type": config.currency_type, + } + ) def main(): @@ -289,8 +309,7 @@ def main(): port = config.listening_port log.info("listening on %s:%d" % (ip, port)) - server = WSGIServer((ip, port), app, - handler_class=WebSocketHandler) + server = WSGIServer((ip, port), app, handler_class=WebSocketHandler) server.serve_forever()