profile filetype checks

Added my Controller as a samba share so I could edit profile files remotely, as OSX is very aggressive about shoving ._ files where you don't want them,
I added a check for that and another check to make sure to only try and open files ending in .json
pull/168/head
Sam 2024-01-23 14:23:57 +00:00
rodzic 7c7a1b648e
commit 96e5919464
2 zmienionych plików z 85 dodań i 65 usunięć

1
.gitignore vendored
Wyświetl plik

@ -8,3 +8,4 @@ thumbs.db
#storage/profiles
#config.py
.idea/*
.DS_Store

Wyświetl plik

@ -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()