Merge pull request #35 from danielperna84/dev

New version
This commit is contained in:
Daniel Perna 2017-04-27 12:07:04 +02:00 committed by GitHub
commit 10a72294a2
4 changed files with 269 additions and 76 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.vscode/

View file

@ -1,3 +1,9 @@
Version 0.1.7 (2017-04-nn)
- Fixed incorrect numeric state option
- Removed check config option (HASS has changed, not useful anymore)
- Added reload for groups, core and automations
- Proper logging on the server side (useful for supervisor usage)
Version 0.1.6 (2017-03-28) Version 0.1.6 (2017-03-28)
- Executing commands on remote machine now possbile (Issue #30) - Executing commands on remote machine now possbile (Issue #30)
- Using icons from https://materialdesignicons.com - Using icons from https://materialdesignicons.com

View file

@ -15,6 +15,7 @@ import signal
import cgi import cgi
import shlex import shlex
import subprocess import subprocess
import logging
from string import Template from string import Template
from http.server import BaseHTTPRequestHandler, HTTPServer from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.request import urllib.request
@ -45,8 +46,15 @@ BANLIMIT = 0
GIT = False GIT = False
### End of options ### End of options
RELEASEURL = "https://api.github.com/repos/danielperna84/hass-poc-configurator/releases/latest" LOGLEVEL = logging.INFO
VERSION = "0.1.6" LOG = logging.getLogger(__name__)
LOG.setLevel(LOGLEVEL)
SO = logging.StreamHandler(sys.stdout)
SO.setLevel(LOGLEVEL)
SO.setFormatter(logging.Formatter('%(levelname)s:%(asctime)s:%(name)s:%(message)s'))
LOG.addHandler(SO)
RELEASEURL = "https://api.github.com/repos/danielperna84/hass-configurator/releases/latest"
VERSION = "0.1.7"
BASEDIR = "." BASEDIR = "."
DEV = False DEV = False
HTTPD = None HTTPD = None
@ -56,7 +64,7 @@ if GIT:
try: try:
from git import Repo as REPO from git import Repo as REPO
except Exception: except Exception:
print("Unable to import Git module") LOG.warn("Unable to import Git module")
INDEX = Template(r"""<!DOCTYPE html> INDEX = Template(r"""<!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
@ -580,7 +588,10 @@ INDEX = Template(r"""<!DOCTYPE html>
<li><a href="#" data-activates="ace_settings" class="ace_settings-collapse">Editor Settings</a></li> <li><a href="#" data-activates="ace_settings" class="ace_settings-collapse">Editor Settings</a></li>
<li><a href="#modal_about">About HASS-Configurator</a></li> <li><a href="#modal_about">About HASS-Configurator</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#modal_check_config">Check HASS Configuration</a></li> <!--<li><a href="#modal_check_config">Check HASS Configuration</a></li>-->
<li><a href="#modal_reload_automations">Reload automations</a></li>
<li><a href="#modal_reload_groups">Reload groups</a></li>
<li><a href="#modal_reload_core">Reload core</a></li>
<li><a href="#modal_restart">Restart HASS</a></li> <li><a href="#modal_restart">Restart HASS</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#modal_exec_command">Execute shell command</a></li> <li><a href="#modal_exec_command">Execute shell command</a></li>
@ -592,7 +603,10 @@ INDEX = Template(r"""<!DOCTYPE html>
<li><a href="#" data-activates="ace_settings" class="ace_settings-collapse">Editor Settings</a></li> <li><a href="#" data-activates="ace_settings" class="ace_settings-collapse">Editor Settings</a></li>
<li><a href="#modal_about">About HASS-Configurator</a></li> <li><a href="#modal_about">About HASS-Configurator</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#modal_check_config">Check HASS Configuration</a></li> <!--<li><a href="#modal_check_config">Check HASS Configuration</a></li>-->
<li><a href="#modal_reload_automations">Reload automations</a></li>
<li><a href="#modal_reload_groups">Reload groups</a></li>
<li><a href="#modal_reload_core">Reload core</a></li>
<li><a href="#modal_restart">Restart HASS</a></li> <li><a href="#modal_restart">Restart HASS</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#modal_exec_command">Execute shell command</a></li> <li><a href="#modal_exec_command">Execute shell command</a></li>
@ -1227,6 +1241,36 @@ INDEX = Template(r"""<!DOCTYPE html>
<a onclick="check_config()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a> <a onclick="check_config()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a>
</div> </div>
</div> </div>
<div id="modal_reload_automations" class="modal">
<div class="modal-content">
<h4 class="grey-text text-darken-3">Reload automations<i class="mdi mdi-settings right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
<p>Do you want to reload the automations?</p>
</div>
<div class="modal-footer">
<a class=" modal-action modal-close waves-effect waves-red btn-flat light-blue-text">No</a>
<a onclick="reload_automations()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a>
</div>
</div>
<div id="modal_reload_groups" class="modal">
<div class="modal-content">
<h4 class="grey-text text-darken-3">Reload groups<i class="mdi mdi-settings right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
<p>Do you want to reload the groups?</p>
</div>
<div class="modal-footer">
<a class=" modal-action modal-close waves-effect waves-red btn-flat light-blue-text">No</a>
<a onclick="reload_groups()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a>
</div>
</div>
<div id="modal_reload_core" class="modal">
<div class="modal-content">
<h4 class="grey-text text-darken-3">Reload core<i class="mdi mdi-settings right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
<p>Do you want to reload the core?</p>
</div>
<div class="modal-footer">
<a class=" modal-action modal-close waves-effect waves-red btn-flat light-blue-text">No</a>
<a onclick="reload_core()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a>
</div>
</div>
<div id="modal_restart" class="modal"> <div id="modal_restart" class="modal">
<div class="modal-content"> <div class="modal-content">
<h4 class="grey-text text-darken-3">Restart<i class="mdi mdi-restart right grey-text text-darken-3" style="font-size: 2rem;"></i></h4> <h4 class="grey-text text-darken-3">Restart<i class="mdi mdi-restart right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
@ -1393,7 +1437,7 @@ INDEX = Template(r"""<!DOCTYPE html>
<option value="" disabled selected>Select trigger platform</option> <option value="" disabled selected>Select trigger platform</option>
<option value="event">Event</option> <option value="event">Event</option>
<option value="mqtt">MQTT</option> <option value="mqtt">MQTT</option>
<option value="numberic_state">Numeric State</option> <option value="numeric_state">Numeric State</option>
<option value="state">State</option> <option value="state">State</option>
<option value="sun">Sun</option> <option value="sun">Sun</option>
<option value="template">Template</option> <option value="template">Template</option>
@ -1491,7 +1535,7 @@ INDEX = Template(r"""<!DOCTYPE html>
<option value="" disabled selected>Select trigger platform</option> <option value="" disabled selected>Select trigger platform</option>
<option value="event">Event</option> <option value="event">Event</option>
<option value="mqtt">MQTT</option> <option value="mqtt">MQTT</option>
<option value="numberic_state">Numeric State</option> <option value="numeric_state">Numeric State</option>
<option value="state">State</option> <option value="state">State</option>
<option value="sun">Sun</option> <option value="sun">Sun</option>
<option value="template">Template</option> <option value="template">Template</option>
@ -2205,11 +2249,32 @@ INDEX = Template(r"""<!DOCTYPE html>
} }
else { else {
var $toastContent = $("<div><pre>" + resp[0].state + "</pre></div>"); var $toastContent = $("<div><pre>" + resp[0].state + "</pre></div>");
Materialize.toast($toastContent, 5000); Materialize.toast($toastContent, 2000);
} }
}); });
} }
function reload_automations() {
$.get("api/reload_automations", function (resp) {
var $toastContent = $("<div>Automations reloaded</div>");
Materialize.toast($toastContent, 2000);
});
}
function reload_groups() {
$.get("api/reload_groups", function (resp) {
var $toastContent = $("<div><pre>Groups reloaded</pre></div>");
Materialize.toast($toastContent, 2000);
});
}
function reload_core() {
$.get("api/reload_core", function (resp) {
var $toastContent = $("<div><pre>Core reloaded</pre></div>");
Materialize.toast($toastContent, 2000);
});
}
function restart() { function restart() {
$.get("api/restart", function (resp) { $.get("api/restart", function (resp) {
if (resp.length == 0) { if (resp.length == 0) {
@ -2596,7 +2661,7 @@ INDEX = Template(r"""<!DOCTYPE html>
def signal_handler(sig, frame): def signal_handler(sig, frame):
global HTTPD global HTTPD
print("Got signal: %s. Shutting down server" % str(sig)) LOG.info("Got signal: %s. Shutting down server" % str(sig))
HTTPD.server_close() HTTPD.server_close()
sys.exit(0) sys.exit(0)
@ -2620,8 +2685,8 @@ def load_settings(settingsfile):
BANLIMIT = settings.get("BANLIMIT", BANLIMIT) BANLIMIT = settings.get("BANLIMIT", BANLIMIT)
DEV = settings.get("DEV", DEV) DEV = settings.get("DEV", DEV)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
print("Not loading static settings") LOG.warn("Not loading static settings")
return False return False
def get_dircontent(path, repo=None): def get_dircontent(path, repo=None):
@ -2637,7 +2702,7 @@ def get_dircontent(path, repo=None):
for element in repo.index.diff("HEAD"): for element in repo.index.diff("HEAD"):
staged["%s%s%s" % (repo.working_dir, os.sep, "%s"%os.sep.join(element.b_path.split('/')))] = element.change_type staged["%s%s%s" % (repo.working_dir, os.sep, "%s"%os.sep.join(element.b_path.split('/')))] = element.change_type
except Exception as err: except Exception as err:
print("Exception: %s" % str(err)) LOG.warn("Exception: %s" % str(err))
for element in repo.index.diff(None): for element in repo.index.diff(None):
unstaged["%s%s%s" % (repo.working_dir, os.sep, "%s"%os.sep.join(element.b_path.split('/')))] = element.change_type unstaged["%s%s%s" % (repo.working_dir, os.sep, "%s"%os.sep.join(element.b_path.split('/')))] = element.change_type
else: else:
@ -2679,8 +2744,8 @@ def get_html():
html = Template(fptr.read()) html = Template(fptr.read())
return html return html
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
print("Delivering embedded HTML") LOG.warn("Delivering embedded HTML")
return INDEX return INDEX
def check_access(clientip): def check_access(clientip):
@ -2697,6 +2762,10 @@ def check_access(clientip):
return False return False
class RequestHandler(BaseHTTPRequestHandler): class RequestHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
LOG.info("%s - %s" % (self.client_address[0], format%args))
return
def do_BLOCK(self): def do_BLOCK(self):
self.send_response(420) self.send_response(420)
self.end_headers() self.end_headers()
@ -2723,7 +2792,7 @@ class RequestHandler(BaseHTTPRequestHandler):
else: else:
content = "File not found" content = "File not found"
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
content = str(err) content = str(err)
self.wfile.write(bytes(content, "utf8")) self.wfile.write(bytes(content, "utf8"))
return return
@ -2733,7 +2802,7 @@ class RequestHandler(BaseHTTPRequestHandler):
try: try:
if filename: if filename:
filename = unquote(filename[0]).encode('utf-8') filename = unquote(filename[0]).encode('utf-8')
print(filename) LOG.info(filename)
if os.path.isfile(os.path.join(BASEDIR.encode('utf-8'), filename)): if os.path.isfile(os.path.join(BASEDIR.encode('utf-8'), filename)):
with open(os.path.join(BASEDIR.encode('utf-8'), filename), 'rb') as fptr: with open(os.path.join(BASEDIR.encode('utf-8'), filename), 'rb') as fptr:
filecontent = fptr.read() filecontent = fptr.read()
@ -2744,7 +2813,7 @@ class RequestHandler(BaseHTTPRequestHandler):
else: else:
content = "File not found" content = "File not found"
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
content = str(err) content = str(err)
self.send_header('Content-type', 'text/text') self.send_header('Content-type', 'text/text')
self.wfile.write(bytes(content, "utf8")) self.wfile.write(bytes(content, "utf8"))
@ -2770,7 +2839,7 @@ class RequestHandler(BaseHTTPRequestHandler):
for branch in repo.branches: for branch in repo.branches:
branches.append(branch.name) branches.append(branch.name)
except Exception as err: except Exception as err:
print("Exception (no repo): %s" % str(err)) LOG.debug("Exception (no repo): %s" % str(err))
dircontent = get_dircontent(dirpath.decode('utf-8'), repo) dircontent = get_dircontent(dirpath.decode('utf-8'), repo)
filedata = {'content': dircontent, filedata = {'content': dircontent,
'abspath': os.path.abspath(dirpath).decode('utf-8'), 'abspath': os.path.abspath(dirpath).decode('utf-8'),
@ -2781,7 +2850,7 @@ class RequestHandler(BaseHTTPRequestHandler):
} }
self.wfile.write(bytes(json.dumps(filedata), "utf8")) self.wfile.write(bytes(json.dumps(filedata), "utf8"))
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
content = str(err) content = str(err)
self.wfile.write(bytes(content, "utf8")) self.wfile.write(bytes(content, "utf8"))
return return
@ -2792,9 +2861,9 @@ class RequestHandler(BaseHTTPRequestHandler):
dirpath = query.get('path', None) dirpath = query.get('path', None)
if dirpath: if dirpath:
dirpath = unquote(dirpath[0]).encode('utf-8') dirpath = unquote(dirpath[0]).encode('utf-8')
print(dirpath) LOG.debug(dirpath)
absp = os.path.abspath(dirpath) absp = os.path.abspath(dirpath)
print(absp) LOG.debug(absp)
if os.path.isdir(dirpath): if os.path.isdir(dirpath):
self.wfile.write(os.path.abspath(dirpath)) self.wfile.write(os.path.abspath(dirpath))
return return
@ -2805,14 +2874,14 @@ class RequestHandler(BaseHTTPRequestHandler):
dirpath = query.get('path', None) dirpath = query.get('path', None)
if dirpath: if dirpath:
dirpath = unquote(dirpath[0]).encode('utf-8') dirpath = unquote(dirpath[0]).encode('utf-8')
print(dirpath) LOG.debug(dirpath)
absp = os.path.abspath(dirpath) absp = os.path.abspath(dirpath)
print(absp) LOG.debug(absp)
if os.path.isdir(dirpath): if os.path.isdir(dirpath):
self.wfile.write(os.path.abspath(os.path.dirname(dirpath))) self.wfile.write(os.path.abspath(os.path.dirname(dirpath)))
return return
elif req.path == '/api/restart': elif req.path == '/api/restart':
print("/api/restart") LOG.info("/api/restart")
self.send_header('Content-type', 'text/json') self.send_header('Content-type', 'text/json')
self.end_headers() self.end_headers()
res = {"restart": False} res = {"restart": False}
@ -2825,14 +2894,14 @@ class RequestHandler(BaseHTTPRequestHandler):
req = urllib.request.Request("%sservices/homeassistant/restart" % HASS_API, headers=headers, method='POST') req = urllib.request.Request("%sservices/homeassistant/restart" % HASS_API, headers=headers, method='POST')
with urllib.request.urlopen(req) as response: with urllib.request.urlopen(req) as response:
res = json.loads(response.read().decode('utf-8')) res = json.loads(response.read().decode('utf-8'))
print(res) LOG.debug(res)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
res['restart'] = str(err) res['restart'] = str(err)
self.wfile.write(bytes(json.dumps(res), "utf8")) self.wfile.write(bytes(json.dumps(res), "utf8"))
return return
elif req.path == '/api/check_config': elif req.path == '/api/check_config':
print("/api/check_config") LOG.info("/api/check_config")
self.send_header('Content-type', 'text/json') self.send_header('Content-type', 'text/json')
self.end_headers() self.end_headers()
res = {"check_config": False} res = {"check_config": False}
@ -2843,11 +2912,71 @@ class RequestHandler(BaseHTTPRequestHandler):
if HASS_API_PASSWORD: if HASS_API_PASSWORD:
headers["x-ha-access"] = HASS_API_PASSWORD headers["x-ha-access"] = HASS_API_PASSWORD
req = urllib.request.Request("%sservices/homeassistant/check_config" % HASS_API, headers=headers, method='POST') req = urllib.request.Request("%sservices/homeassistant/check_config" % HASS_API, headers=headers, method='POST')
with urllib.request.urlopen(req) as response: # with urllib.request.urlopen(req) as response:
res = json.loads(response.read().decode('utf-8')) # print(json.loads(response.read().decode('utf-8')))
print(res) # res['service'] = "called successfully"
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
res['restart'] = str(err)
self.wfile.write(bytes(json.dumps(res), "utf8"))
return
elif req.path == '/api/reload_automations':
LOG.info("/api/reload_automations")
self.send_header('Content-type', 'text/json')
self.end_headers()
res = {"reload_automations": False}
try:
headers = {
"Content-Type": "application/json"
}
if HASS_API_PASSWORD:
headers["x-ha-access"] = HASS_API_PASSWORD
req = urllib.request.Request("%sservices/automation/reload" % HASS_API, headers=headers, method='POST')
with urllib.request.urlopen(req) as response:
LOG.debug(json.loads(response.read().decode('utf-8')))
res['service'] = "called successfully"
except Exception as err:
LOG.warn(err)
res['restart'] = str(err)
self.wfile.write(bytes(json.dumps(res), "utf8"))
return
elif req.path == '/api/reload_groups':
LOG.info("/api/reload_groups")
self.send_header('Content-type', 'text/json')
self.end_headers()
res = {"reload_groups": False}
try:
headers = {
"Content-Type": "application/json"
}
if HASS_API_PASSWORD:
headers["x-ha-access"] = HASS_API_PASSWORD
req = urllib.request.Request("%sservices/group/reload" % HASS_API, headers=headers, method='POST')
with urllib.request.urlopen(req) as response:
LOG.debug(json.loads(response.read().decode('utf-8')))
res['service'] = "called successfully"
except Exception as err:
LOG.warn(err)
res['restart'] = str(err)
self.wfile.write(bytes(json.dumps(res), "utf8"))
return
elif req.path == '/api/reload_core':
LOG.info("/api/reload_core")
self.send_header('Content-type', 'text/json')
self.end_headers()
res = {"reload_core": False}
try:
headers = {
"Content-Type": "application/json"
}
if HASS_API_PASSWORD:
headers["x-ha-access"] = HASS_API_PASSWORD
req = urllib.request.Request("%sservices/homeassistant/reload_core_config" % HASS_API, headers=headers, method='POST')
with urllib.request.urlopen(req) as response:
LOG.debug(json.loads(response.read().decode('utf-8')))
res['service'] = "called successfully"
except Exception as err:
LOG.warn(err)
res['restart'] = str(err) res['restart'] = str(err)
self.wfile.write(bytes(json.dumps(res), "utf8")) self.wfile.write(bytes(json.dumps(res), "utf8"))
return return
@ -2867,8 +2996,8 @@ class RequestHandler(BaseHTTPRequestHandler):
boot = response.read().decode('utf-8') boot = response.read().decode('utf-8')
except Exception as err: except Exception as err:
print("Exception getting bootstrap") LOG.warn("Exception getting bootstrap")
print(err) LOG.warn(err)
color = "green" color = "green"
try: try:
@ -2877,8 +3006,8 @@ class RequestHandler(BaseHTTPRequestHandler):
if VERSION != latest: if VERSION != latest:
color = "red" color = "red"
except Exception as err: except Exception as err:
print("Exception getting release") LOG.warn("Exception getting release")
print(err) LOG.warn(err)
html = get_html().safe_substitute(bootstrap=boot, html = get_html().safe_substitute(bootstrap=boot,
current=VERSION, current=VERSION,
versionclass=color, versionclass=color,
@ -2906,7 +3035,7 @@ class RequestHandler(BaseHTTPRequestHandler):
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'filename' in postvars.keys() and 'text' in postvars.keys(): if 'filename' in postvars.keys() and 'text' in postvars.keys():
@ -2925,7 +3054,7 @@ class RequestHandler(BaseHTTPRequestHandler):
return return
except Exception as err: except Exception as err:
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
print(err) LOG.warn(err)
else: else:
response['message'] = "Missing filename or text" response['message'] = "Missing filename or text"
elif req.path == '/api/upload': elif req.path == '/api/upload':
@ -2962,7 +3091,7 @@ class RequestHandler(BaseHTTPRequestHandler):
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'path' in postvars.keys(): if 'path' in postvars.keys():
@ -2983,20 +3112,20 @@ class RequestHandler(BaseHTTPRequestHandler):
self.wfile.write(bytes(json.dumps(response), "utf8")) self.wfile.write(bytes(json.dumps(response), "utf8"))
return return
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
except Exception as err: except Exception as err:
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
print(err) LOG.warn(err)
else: else:
response['message'] = "Missing filename or text" response['message'] = "Missing filename or text"
elif req.path == '/api/exec_command': elif req.path == '/api/exec_command':
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'command' in postvars.keys(): if 'command' in postvars.keys():
@ -3020,30 +3149,30 @@ class RequestHandler(BaseHTTPRequestHandler):
try: try:
response['stdout'] = stdout.decode(sys.getdefaultencoding()) response['stdout'] = stdout.decode(sys.getdefaultencoding())
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['stdout'] = stdout.decode("utf-8", errors="replace") response['stdout'] = stdout.decode("utf-8", errors="replace")
try: try:
response['stderr'] = stderr.decode(sys.getdefaultencoding()) response['stderr'] = stderr.decode(sys.getdefaultencoding())
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['stderr'] = stderr.decode("utf-8", errors="replace") response['stderr'] = stderr.decode("utf-8", errors="replace")
self.wfile.write(bytes(json.dumps(response), "utf8")) self.wfile.write(bytes(json.dumps(response), "utf8"))
return return
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
except Exception as err: except Exception as err:
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
print(err) LOG.warn(err)
else: else:
response['message'] = "Missing command" response['message'] = "Missing command"
elif req.path == '/api/gitadd': elif req.path == '/api/gitadd':
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'path' in postvars.keys(): if 'path' in postvars.keys():
@ -3063,20 +3192,20 @@ class RequestHandler(BaseHTTPRequestHandler):
self.wfile.write(bytes(json.dumps(response), "utf8")) self.wfile.write(bytes(json.dumps(response), "utf8"))
return return
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
except Exception as err: except Exception as err:
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
print(err) LOG.warn(err)
else: else:
response['message'] = "Missing filename" response['message'] = "Missing filename"
elif req.path == '/api/commit': elif req.path == '/api/commit':
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'path' in postvars.keys() and 'message' in postvars.keys(): if 'path' in postvars.keys() and 'message' in postvars.keys():
@ -3098,18 +3227,18 @@ class RequestHandler(BaseHTTPRequestHandler):
except Exception as err: except Exception as err:
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
print(response) LOG.debug(response)
except Exception as err: except Exception as err:
response['message'] = "Not a git repository: %s" % (str(err)) response['message'] = "Not a git repository: %s" % (str(err))
print("Exception (no repo): %s" % str(err)) LOG.warn("Exception (no repo): %s" % str(err))
else: else:
response['message'] = "Missing path" response['message'] = "Missing path"
elif req.path == '/api/checkout': elif req.path == '/api/checkout':
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'path' in postvars.keys() and 'branch' in postvars.keys(): if 'path' in postvars.keys() and 'branch' in postvars.keys():
@ -3132,18 +3261,18 @@ class RequestHandler(BaseHTTPRequestHandler):
except Exception as err: except Exception as err:
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
print(response) LOG.warn(response)
except Exception as err: except Exception as err:
response['message'] = "Not a git repository: %s" % (str(err)) response['message'] = "Not a git repository: %s" % (str(err))
print("Exception (no repo): %s" % str(err)) LOG.warn("Exception (no repo): %s" % str(err))
else: else:
response['message'] = "Missing path or branch" response['message'] = "Missing path or branch"
elif req.path == '/api/newbranch': elif req.path == '/api/newbranch':
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'path' in postvars.keys() and 'branch' in postvars.keys(): if 'path' in postvars.keys() and 'branch' in postvars.keys():
@ -3165,18 +3294,18 @@ class RequestHandler(BaseHTTPRequestHandler):
except Exception as err: except Exception as err:
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
print(response) LOG.warn(response)
except Exception as err: except Exception as err:
response['message'] = "Not a git repository: %s" % (str(err)) response['message'] = "Not a git repository: %s" % (str(err))
print("Exception (no repo): %s" % str(err)) LOG.warn("Exception (no repo): %s" % str(err))
else: else:
response['message'] = "Missing path or branch" response['message'] = "Missing path or branch"
elif req.path == '/api/init': elif req.path == '/api/init':
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'path' in postvars.keys(): if 'path' in postvars.keys():
@ -3196,18 +3325,18 @@ class RequestHandler(BaseHTTPRequestHandler):
except Exception as err: except Exception as err:
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
print(response) LOG.warn(response)
except Exception as err: except Exception as err:
response['message'] = "Not a git repository: %s" % (str(err)) response['message'] = "Not a git repository: %s" % (str(err))
print("Exception (no repo): %s" % str(err)) LOG.warn("Exception (no repo): %s" % str(err))
else: else:
response['message'] = "Missing path or branch" response['message'] = "Missing path or branch"
elif req.path == '/api/newfolder': elif req.path == '/api/newfolder':
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'path' in postvars.keys() and 'name' in postvars.keys(): if 'path' in postvars.keys() and 'name' in postvars.keys():
@ -3226,17 +3355,17 @@ class RequestHandler(BaseHTTPRequestHandler):
self.wfile.write(bytes(json.dumps(response), "utf8")) self.wfile.write(bytes(json.dumps(response), "utf8"))
return return
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
except Exception as err: except Exception as err:
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
print(err) LOG.warn(err)
elif req.path == '/api/newfile': elif req.path == '/api/newfile':
try: try:
postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1) postvars = parse_qs(self.rfile.read(length).decode('utf-8'), keep_blank_values=1)
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
postvars = {} postvars = {}
if 'path' in postvars.keys() and 'name' in postvars.keys(): if 'path' in postvars.keys() and 'name' in postvars.keys():
@ -3256,12 +3385,12 @@ class RequestHandler(BaseHTTPRequestHandler):
self.wfile.write(bytes(json.dumps(response), "utf8")) self.wfile.write(bytes(json.dumps(response), "utf8"))
return return
except Exception as err: except Exception as err:
print(err) LOG.warn(err)
response['error'] = True response['error'] = True
response['message'] = str(err) response['message'] = str(err)
except Exception as err: except Exception as err:
response['message'] = "%s" % (str(err)) response['message'] = "%s" % (str(err))
print(err) LOG.warn(err)
else: else:
response['message'] = "Missing filename or text" response['message'] = "Missing filename or text"
else: else:
@ -3274,7 +3403,7 @@ class RequestHandler(BaseHTTPRequestHandler):
class AuthHandler(RequestHandler): class AuthHandler(RequestHandler):
def do_AUTHHEAD(self): def do_AUTHHEAD(self):
print("Requesting authorization") LOG.info("Requesting authorization")
self.send_response(401) self.send_response(401)
self.send_header('WWW-Authenticate', 'Basic realm=\"HASS-PoC-Configurator\"') self.send_header('WWW-Authenticate', 'Basic realm=\"HASS-PoC-Configurator\"')
self.send_header('Content-type', 'text/html') self.send_header('Content-type', 'text/html')
@ -3296,7 +3425,7 @@ class AuthHandler(RequestHandler):
if BANLIMIT: if BANLIMIT:
bancounter = FAIL2BAN_IPS.get(self.client_address[0], 1) bancounter = FAIL2BAN_IPS.get(self.client_address[0], 1)
if bancounter >= BANLIMIT: if bancounter >= BANLIMIT:
print("Blocking access from %s" % self.client_address[0]) LOG.warn("Blocking access from %s" % self.client_address[0])
self.do_BLOCK() self.do_BLOCK()
return return
else: else:
@ -3321,7 +3450,7 @@ class AuthHandler(RequestHandler):
if BANLIMIT: if BANLIMIT:
bancounter = FAIL2BAN_IPS.get(self.client_address[0], 1) bancounter = FAIL2BAN_IPS.get(self.client_address[0], 1)
if bancounter >= BANLIMIT: if bancounter >= BANLIMIT:
print("Blocking access from %s" % self.client_address[0]) LOG.warn("Blocking access from %s" % self.client_address[0])
self.do_BLOCK() self.do_BLOCK()
return return
else: else:
@ -3334,7 +3463,7 @@ def main(args):
global HTTPD, CREDENTIALS global HTTPD, CREDENTIALS
if args: if args:
load_settings(args[0]) load_settings(args[0])
print("Starting server") LOG.info("Starting server")
server_address = (LISTENIP, LISTENPORT) server_address = (LISTENIP, LISTENPORT)
if CREDENTIALS: if CREDENTIALS:
CREDENTIALS = base64.b64encode(bytes(CREDENTIALS, "utf-8")) CREDENTIALS = base64.b64encode(bytes(CREDENTIALS, "utf-8"))
@ -3349,7 +3478,7 @@ def main(args):
certfile=SSL_CERTIFICATE, certfile=SSL_CERTIFICATE,
keyfile=SSL_KEY, keyfile=SSL_KEY,
server_side=True) server_side=True)
print('Listening on: %s://%s:%i' % ('https' if SSL_CERTIFICATE else 'http', LOG.info('Listening on: %s://%s:%i' % ('https' if SSL_CERTIFICATE else 'http',
LISTENIP, LISTENIP,
LISTENPORT)) LISTENPORT))
if BASEPATH: if BASEPATH:

View file

@ -521,7 +521,10 @@
<li><a href="#" data-activates="ace_settings" class="ace_settings-collapse">Editor Settings</a></li> <li><a href="#" data-activates="ace_settings" class="ace_settings-collapse">Editor Settings</a></li>
<li><a href="#modal_about">About HASS-Configurator</a></li> <li><a href="#modal_about">About HASS-Configurator</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#modal_check_config">Check HASS Configuration</a></li> <!--<li><a href="#modal_check_config">Check HASS Configuration</a></li>-->
<li><a href="#modal_reload_automations">Reload automations</a></li>
<li><a href="#modal_reload_groups">Reload groups</a></li>
<li><a href="#modal_reload_core">Reload core</a></li>
<li><a href="#modal_restart">Restart HASS</a></li> <li><a href="#modal_restart">Restart HASS</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#modal_exec_command">Execute shell command</a></li> <li><a href="#modal_exec_command">Execute shell command</a></li>
@ -533,7 +536,10 @@
<li><a href="#" data-activates="ace_settings" class="ace_settings-collapse">Editor Settings</a></li> <li><a href="#" data-activates="ace_settings" class="ace_settings-collapse">Editor Settings</a></li>
<li><a href="#modal_about">About HASS-Configurator</a></li> <li><a href="#modal_about">About HASS-Configurator</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#modal_check_config">Check HASS Configuration</a></li> <!--<li><a href="#modal_check_config">Check HASS Configuration</a></li>-->
<li><a href="#modal_reload_automations">Reload automations</a></li>
<li><a href="#modal_reload_groups">Reload groups</a></li>
<li><a href="#modal_reload_core">Reload core</a></li>
<li><a href="#modal_restart">Restart HASS</a></li> <li><a href="#modal_restart">Restart HASS</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#modal_exec_command">Execute shell command</a></li> <li><a href="#modal_exec_command">Execute shell command</a></li>
@ -1168,6 +1174,36 @@
<a onclick="check_config()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a> <a onclick="check_config()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a>
</div> </div>
</div> </div>
<div id="modal_reload_automations" class="modal">
<div class="modal-content">
<h4 class="grey-text text-darken-3">Reload automations<i class="mdi mdi-settings right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
<p>Do you want to reload the automations?</p>
</div>
<div class="modal-footer">
<a class=" modal-action modal-close waves-effect waves-red btn-flat light-blue-text">No</a>
<a onclick="reload_automations()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a>
</div>
</div>
<div id="modal_reload_groups" class="modal">
<div class="modal-content">
<h4 class="grey-text text-darken-3">Reload groups<i class="mdi mdi-settings right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
<p>Do you want to reload the groups?</p>
</div>
<div class="modal-footer">
<a class=" modal-action modal-close waves-effect waves-red btn-flat light-blue-text">No</a>
<a onclick="reload_groups()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a>
</div>
</div>
<div id="modal_reload_core" class="modal">
<div class="modal-content">
<h4 class="grey-text text-darken-3">Reload core<i class="mdi mdi-settings right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
<p>Do you want to reload the core?</p>
</div>
<div class="modal-footer">
<a class=" modal-action modal-close waves-effect waves-red btn-flat light-blue-text">No</a>
<a onclick="reload_core()" class=" modal-action modal-close waves-effect waves-green btn-flat light-blue-text">Yes</a>
</div>
</div>
<div id="modal_restart" class="modal"> <div id="modal_restart" class="modal">
<div class="modal-content"> <div class="modal-content">
<h4 class="grey-text text-darken-3">Restart<i class="mdi mdi-restart right grey-text text-darken-3" style="font-size: 2rem;"></i></h4> <h4 class="grey-text text-darken-3">Restart<i class="mdi mdi-restart right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
@ -1334,7 +1370,7 @@
<option value="" disabled selected>Select trigger platform</option> <option value="" disabled selected>Select trigger platform</option>
<option value="event">Event</option> <option value="event">Event</option>
<option value="mqtt">MQTT</option> <option value="mqtt">MQTT</option>
<option value="numberic_state">Numeric State</option> <option value="numeric_state">Numeric State</option>
<option value="state">State</option> <option value="state">State</option>
<option value="sun">Sun</option> <option value="sun">Sun</option>
<option value="template">Template</option> <option value="template">Template</option>
@ -1432,7 +1468,7 @@
<option value="" disabled selected>Select trigger platform</option> <option value="" disabled selected>Select trigger platform</option>
<option value="event">Event</option> <option value="event">Event</option>
<option value="mqtt">MQTT</option> <option value="mqtt">MQTT</option>
<option value="numberic_state">Numeric State</option> <option value="numeric_state">Numeric State</option>
<option value="state">State</option> <option value="state">State</option>
<option value="sun">Sun</option> <option value="sun">Sun</option>
<option value="template">Template</option> <option value="template">Template</option>
@ -2146,11 +2182,32 @@
} }
else { else {
var $toastContent = $("<div><pre>" + resp[0].state + "</pre></div>"); var $toastContent = $("<div><pre>" + resp[0].state + "</pre></div>");
Materialize.toast($toastContent, 5000); Materialize.toast($toastContent, 2000);
} }
}); });
} }
function reload_automations() {
$.get("api/reload_automations", function (resp) {
var $toastContent = $("<div>Automations reloaded</div>");
Materialize.toast($toastContent, 2000);
});
}
function reload_groups() {
$.get("api/reload_groups", function (resp) {
var $toastContent = $("<div><pre>Groups reloaded</pre></div>");
Materialize.toast($toastContent, 2000);
});
}
function reload_core() {
$.get("api/reload_core", function (resp) {
var $toastContent = $("<div><pre>Core reloaded</pre></div>");
Materialize.toast($toastContent, 2000);
});
}
function restart() { function restart() {
$.get("api/restart", function (resp) { $.get("api/restart", function (resp) {
if (resp.length == 0) { if (resp.length == 0) {