Merge pull request #98 from danielperna84/dev

0.2.9
This commit is contained in:
Daniel Perna 2018-06-22 16:23:56 +02:00 committed by GitHub
commit 49bd67d3f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 167 additions and 84 deletions

View file

@ -43,6 +43,8 @@ The IP address the service is listening on. By default it is binding to `0.0.0.0
The port the service is listening on. By default it is using 3218, but you can change this if you need to. The port the service is listening on. By default it is using 3218, but you can change this if you need to.
#### BASEPATH (string) #### BASEPATH (string)
It is possible to place configurator.py somewhere else. Set the `BASEPATH` to something like `"/home/homeassistant/.homeassistant"`, and no matter where you are running the configurator from, it will start serving files from there. This is needed if you plan on running the configurator with systemd. It is possible to place configurator.py somewhere else. Set the `BASEPATH` to something like `"/home/homeassistant/.homeassistant"`, and no matter where you are running the configurator from, it will start serving files from there. This is needed if you plan on running the configurator with systemd.
#### ENFORCE_BASEPATH (bool)
Set ENFORCE_BASEPATH to `True` to lock the configurator into the basepath and thereby prevent it from opening files outside of the BASEPATH
#### SSL_CERTIFICATE / SSL_KEY (string) #### SSL_CERTIFICATE / SSL_KEY (string)
If you're using SSL, set the paths to your SSL files here. This is similar to the SSL setup you can do in HASS. If you're using SSL, set the paths to your SSL files here. This is similar to the SSL setup you can do in HASS.
#### HASS_API (string) #### HASS_API (string)

View file

@ -1,3 +1,13 @@
Version 0.2.9 (2018-06-22)
- Material Icons and HASS-help now open in new tab instead of modal (Issues #85 and #34) @danielperna84
- Open file by URL (Issue #95) @danielperna84
- Added ENFORCE_BASEPATH option (Issue #68) @danielperna84
- Cosmetic fix for scaled viewports @danielperna84
- Added search-function for entities (Issue #99) @danielperna84
- Updated Ace Editor to 1.3.3
- Updated jQuery to 3.3.1
- Updated js-yaml to 3.12.0
Version 0.2.8 (2018-04-23) Version 0.2.8 (2018-04-23)
- Updated CDN libraries @jmart518 - Updated CDN libraries @jmart518
- Cosmetic improvements @jmart518 - Cosmetic improvements @jmart518

View file

@ -30,6 +30,9 @@ LISTENPORT = 3218
# Set BASEPATH to something like "/home/hass/.homeassistant/" if you're not # Set BASEPATH to something like "/home/hass/.homeassistant/" if you're not
# running the configurator from that path # running the configurator from that path
BASEPATH = None BASEPATH = None
# Set ENFORCE_BASEPATH to True to lock the configurator into the basepath and
# thereby prevent it from opening files outside of the BASEPATH
ENFORCE_BASEPATH = False
# Set the paths to a certificate and the key if you're using SSL, # Set the paths to a certificate and the key if you're using SSL,
# e.g "/etc/ssl/certs/mycert.pem" # e.g "/etc/ssl/certs/mycert.pem"
SSL_CERTIFICATE = None SSL_CERTIFICATE = None
@ -74,7 +77,7 @@ SO.setFormatter(
logging.Formatter('%(levelname)s:%(asctime)s:%(name)s:%(message)s')) logging.Formatter('%(levelname)s:%(asctime)s:%(name)s:%(message)s'))
LOG.addHandler(SO) LOG.addHandler(SO)
RELEASEURL = "https://api.github.com/repos/danielperna84/hass-configurator/releases/latest" RELEASEURL = "https://api.github.com/repos/danielperna84/hass-configurator/releases/latest"
VERSION = "0.2.8" VERSION = "0.2.9"
BASEDIR = "." BASEDIR = "."
DEV = False DEV = False
HTTPD = None HTTPD = None
@ -201,7 +204,7 @@ INDEX = Template(r"""<!DOCTYPE html>
color: #616161 !important; color: #616161 !important;
font-weight: 400; font-weight: 400;
display: inline-block; display: inline-block;
width: 185px; width: 182px;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
cursor: pointer; cursor: pointer;
@ -339,7 +342,7 @@ INDEX = Template(r"""<!DOCTYPE html>
box-shadow: 0 1px 0 0 #03a9f4 !important; box-shadow: 0 1px 0 0 #03a9f4 !important;
} }
#modal_acekeyboard, #modal_components, #modal_icons { #modal_acekeyboard {
top: auto; top: auto;
width: 96%; width: 96%;
min-height: 96%; min-height: 96%;
@ -575,9 +578,9 @@ INDEX = Template(r"""<!DOCTYPE html>
height: auto; height: auto;
} }
</style> </style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js" type="text/javascript" charset="utf-8"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ext-modelist.js" type="text/javascript" charset="utf-8"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ext-modelist.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ext-language_tools.js" type="text/javascript" charset="utf-8"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ext-language_tools.js" type="text/javascript" charset="utf-8"></script>
</head> </head>
<body> <body>
<div class="preloader-background"> <div class="preloader-background">
@ -652,10 +655,10 @@ INDEX = Template(r"""<!DOCTYPE html>
</header> </header>
<main> <main>
<ul id="dropdown_menu" class="dropdown-content z-depth-4"> <ul id="dropdown_menu" class="dropdown-content z-depth-4">
<li><a onclick="localStorage.setItem('new_tab', true);window.open(window.location.href, '_blank');">New tab</a></li> <li><a onclick="localStorage.setItem('new_tab', true);window.open(window.location.origin+window.location.pathname, '_blank');">New tab</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a class="modal-trigger" target="_blank" href="#modal_components">Components</a></li> <li><a target="_blank" href="https://home-assistant.io/components/">Components</a></li>
<li><a class="modal-trigger" target="_blank" href="#modal_icons">Material Icons</a></li> <li><a target="_blank" href="https://materialdesignicons.com/">Material Icons</a></li>
<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 class="modal-trigger" href="#modal_netstat" onclick="get_netstat()">Network status</a></li> <li><a class="modal-trigger" href="#modal_netstat" onclick="get_netstat()">Network status</a></li>
<li><a class="modal-trigger" href="#modal_about">About HASS-Configurator</a></li> <li><a class="modal-trigger" href="#modal_about">About HASS-Configurator</a></li>
@ -671,7 +674,7 @@ INDEX = Template(r"""<!DOCTYPE html>
<li><a class="modal-trigger" href="#modal_exec_command">Execute shell command</a></li> <li><a class="modal-trigger" href="#modal_exec_command">Execute shell command</a></li>
</ul> </ul>
<ul id="dropdown_menu_mobile" class="dropdown-content z-depth-4"> <ul id="dropdown_menu_mobile" class="dropdown-content z-depth-4">
<li><a onclick="localStorage.setItem('new_tab', true);window.open(window.location.href, '_blank');">New tab</a></li> <li><a onclick="localStorage.setItem('new_tab', true);window.open(window.location.origin+window.location.pathname, '_blank');">New tab</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a target="_blank" href="https://home-assistant.io/help/">Help</a></li> <li><a target="_blank" href="https://home-assistant.io/help/">Help</a></li>
<li><a target="_blank" href="https://home-assistant.io/components/">Components</a></li> <li><a target="_blank" href="https://home-assistant.io/components/">Components</a></li>
@ -700,24 +703,6 @@ INDEX = Template(r"""<!DOCTYPE html>
<li><a class="modal-trigger" href="#modal_commit" class="nowrap waves-effect">git commit</a></li> <li><a class="modal-trigger" href="#modal_commit" class="nowrap waves-effect">git commit</a></li>
<li><a class="modal-trigger" href="#modal_push" class="nowrap waves-effect">git push</a></li> <li><a class="modal-trigger" href="#modal_push" class="nowrap waves-effect">git push</a></li>
</ul> </ul>
<div id="modal_components" class="modal bottom-sheet modal-fixed-footer">
<div class="modal-content_nopad">
<iframe src="https://home-assistant.io/components/" style="height: 90vh; width: 100vw"> </iframe>
<a target="_blank" href="https://home-assistant.io/components/" class="hide-on-med-and-down modal_btn waves-effect btn-large btn-flat left"><i class="material-icons">launch</i></a>
</div>
<div class="modal-footer">
<a class="modal-action modal-close waves-effect btn-flat Right light-blue-text">Close</a>
</div>
</div>
<div id="modal_icons" class="modal bottom-sheet modal-fixed-footer">
<div class="modal-content_nopad">
<iframe src="https://materialdesignicons.com/" style="height: 90vh; width: 100vw"> </iframe>
<a target="_blank" href="https://materialdesignicons.com/" class="hide-on-med-and-down modal_btn waves-effect btn-large btn-flat left"><i class="material-icons">launch</i></a>
</div>
<div class="modal-footer">
<a class="modal-action modal-close waves-effect btn-flat Right light-blue-text">Close</a>
</div>
</div>
<div id="modal_acekeyboard" class="modal bottom-sheet modal-fixed-footer"> <div id="modal_acekeyboard" class="modal bottom-sheet modal-fixed-footer">
<div class="modal-content centered"> <div class="modal-content centered">
<h4 class="grey-text text-darken-3">Ace Keyboard Shortcuts<i class="mdi mdi-keyboard right grey-text text-darken-3" style="font-size: 2rem;"></i></h4> <h4 class="grey-text text-darken-3">Ace Keyboard Shortcuts<i class="mdi mdi-keyboard right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
@ -1684,6 +1669,10 @@ INDEX = Template(r"""<!DOCTYPE html>
<select id="events" onchange="insert(this.value)"></select> <select id="events" onchange="insert(this.value)"></select>
<label>Events</label> <label>Events</label>
</div> </div>
<div class="input-field col s12">
<input type="text" id="entities-search" class="autocomplete" placeholder="sensor.example">
<label>Search entity</label>
</div>
<div class="input-field col s12"> <div class="input-field col s12">
<select id="entities" onchange="insert(this.value)"></select> <select id="entities" onchange="insert(this.value)"></select>
<label>Entities</label> <label>Entities</label>
@ -1783,6 +1772,10 @@ INDEX = Template(r"""<!DOCTYPE html>
<select id="events_side" onchange="insert(this.value)"></select> <select id="events_side" onchange="insert(this.value)"></select>
<label>Events</label> <label>Events</label>
</div> </div>
<div class="input-field col s12">
<input type="text" id="entities-search_side" class="autocomplete" placeholder="sensor.example">
<label>Search entity</label>
</div>
<div class="input-field col s12"> <div class="input-field col s12">
<select id="entities_side" onchange="insert(this.value)"></select> <select id="entities_side" onchange="insert(this.value)"></select>
<label>Entities</label> <label>Entities</label>
@ -2114,14 +2107,14 @@ INDEX = Template(r"""<!DOCTYPE html>
<input id="wrap_limit" type="number" onchange="editor.setOption('wrap', parseInt(this.value))" min="1" value="80"> <input id="wrap_limit" type="number" onchange="editor.setOption('wrap', parseInt(this.value))" min="1" value="80">
<label class="active" for="wrap_limit">Wrap Limit</label> <label class="active" for="wrap_limit">Wrap Limit</label>
</div> <a class="waves-effect waves-light btn light-blue" onclick="save_ace_settings()">Save Settings Locally</a> </div> <a class="waves-effect waves-light btn light-blue" onclick="save_ace_settings()">Save Settings Locally</a>
<p class="center col s12"> Ace Editor 1.2.9 </p> <p class="center col s12"> Ace Editor 1.3.3 </p>
</div> </div>
</ul> </ul>
</div> </div>
</main> </main>
<input type="hidden" id="fb_currentfile" value="" /> <input type="hidden" id="fb_currentfile" value="" />
<!-- Scripts --> <!-- Scripts -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
<script> <script>
function ws_connect() { function ws_connect() {
@ -2175,6 +2168,7 @@ INDEX = Template(r"""<!DOCTYPE html>
} }
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
var init_loadfile = $loadfile;
var global_current_filepath = null; var global_current_filepath = null;
var global_current_filename = null; var global_current_filename = null;
@ -2276,23 +2270,51 @@ INDEX = Template(r"""<!DOCTYPE html>
$(document).on('click', '.drag-target', function(){$('.button-collapse').sideNav('hide');}) $(document).on('click', '.drag-target', function(){$('.button-collapse').sideNav('hide');})
listdir('.'); listdir('.');
document.getElementById('savePrompt').checked = get_save_prompt(); document.getElementById('savePrompt').checked = get_save_prompt();
var entities_search = new Object();
if (states_list) {
for (var i = 0; i < states_list.length; i++) {
entities_search[states_list[i].attributes.friendly_name + ' (' + states_list[i].entity_id + ')'] = null;
}
}
$('#entities-search').autocomplete({
data: entities_search,
limit: 40,
onAutocomplete: function(val) {
insert(val.split("(")[1].split(")")[0]);
},
minLength: 1,
});
$('#entities-search_side').autocomplete({
data: entities_search,
limit: 40,
onAutocomplete: function(val) {
insert(val.split("(")[1].split(")")[0]);
},
minLength: 1,
});
}); });
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
$('.preloader-background').delay(800).fadeOut('slow'); $('.preloader-background').delay(800).fadeOut('slow');
$('.preloader-wrapper').delay(800).fadeOut('slow'); $('.preloader-wrapper').delay(800).fadeOut('slow');
if (!localStorage.getItem("new_tab")) { if (init_loadfile) {
var old_file = localStorage.getItem("current_file"); init_loadfile_name = init_loadfile.split('/').pop();
if (old_file) { loadfile(init_loadfile, init_loadfile_name);
old_file = JSON.parse(old_file);
loadfile(old_file.current_filepath, old_file.current_filename);
}
} }
else { else {
localStorage.removeItem("current_file"); if (!localStorage.getItem("new_tab")) {
var old_file = localStorage.getItem("current_file");
if (old_file) {
old_file = JSON.parse(old_file);
loadfile(old_file.current_filepath, old_file.current_filename);
}
}
else {
localStorage.removeItem("current_file");
}
localStorage.removeItem("new_tab");
} }
localStorage.removeItem("new_tab");
}); });
</script> </script>
<script> <script>
@ -2398,7 +2420,12 @@ INDEX = Template(r"""<!DOCTYPE html>
function listdir(path) { function listdir(path) {
$.get(encodeURI("api/listdir?path=" + path), function(data) { $.get(encodeURI("api/listdir?path=" + path), function(data) {
renderpath(data); if (!data.error) {
renderpath(data);
}
else {
console.log("Permission denied.");
}
}); });
document.getElementById("slide-out").scrollTop = 0; document.getElementById("slide-out").scrollTop = 0;
} }
@ -3264,7 +3291,7 @@ INDEX = Template(r"""<!DOCTYPE html>
} }
</script> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.10.0/js-yaml.js" type="text/javascript" charset="utf-8"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.12.0/js-yaml.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript"> <script type="text/javascript">
var lint_timeout; var lint_timeout;
var lint_status = $('#lint-status'); // speed optimization var lint_status = $('#lint-status'); // speed optimization
@ -3325,7 +3352,7 @@ def signal_handler(sig, frame):
def load_settings(settingsfile): def load_settings(settingsfile):
global LISTENIP, LISTENPORT, BASEPATH, SSL_CERTIFICATE, SSL_KEY, HASS_API, \ global LISTENIP, LISTENPORT, BASEPATH, SSL_CERTIFICATE, SSL_KEY, HASS_API, \
HASS_API_PASSWORD, CREDENTIALS, ALLOWED_NETWORKS, BANNED_IPS, BANLIMIT, \ HASS_API_PASSWORD, CREDENTIALS, ALLOWED_NETWORKS, BANNED_IPS, BANLIMIT, \
DEV, IGNORE_PATTERN, DIRSFIRST, SESAME, VERIFY_HOSTNAME DEV, IGNORE_PATTERN, DIRSFIRST, SESAME, VERIFY_HOSTNAME, ENFORCE_BASEPATH
try: try:
if os.path.isfile(settingsfile): if os.path.isfile(settingsfile):
with open(settingsfile) as fptr: with open(settingsfile) as fptr:
@ -3333,6 +3360,7 @@ def load_settings(settingsfile):
LISTENIP = settings.get("LISTENIP", LISTENIP) LISTENIP = settings.get("LISTENIP", LISTENIP)
LISTENPORT = settings.get("LISTENPORT", LISTENPORT) LISTENPORT = settings.get("LISTENPORT", LISTENPORT)
BASEPATH = settings.get("BASEPATH", BASEPATH) BASEPATH = settings.get("BASEPATH", BASEPATH)
ENFORCE_BASEPATH = settings.get("ENFORCE_BASEPATH", ENFORCE_BASEPATH)
SSL_CERTIFICATE = settings.get("SSL_CERTIFICATE", SSL_CERTIFICATE) SSL_CERTIFICATE = settings.get("SSL_CERTIFICATE", SSL_CERTIFICATE)
SSL_KEY = settings.get("SSL_KEY", SSL_KEY) SSL_KEY = settings.get("SSL_KEY", SSL_KEY)
HASS_API = settings.get("HASS_API", HASS_API) HASS_API = settings.get("HASS_API", HASS_API)
@ -3351,6 +3379,11 @@ def load_settings(settingsfile):
LOG.warning("Not loading static settings") LOG.warning("Not loading static settings")
return False return False
def is_safe_path(basedir, path, follow_symlinks=True):
if follow_symlinks:
return os.path.realpath(path).startswith(basedir)
return os.path.abspath(path).startswith(basedir)
def get_dircontent(path, repo=None): def get_dircontent(path, repo=None):
dircontent = [] dircontent = []
if repo: if repo:
@ -3487,6 +3520,8 @@ class RequestHandler(BaseHTTPRequestHandler):
try: try:
if filename: if filename:
filename = unquote(filename[0]).encode('utf-8') filename = unquote(filename[0]).encode('utf-8')
if ENFORCE_BASEPATH and not is_safe_path(BASEPATH.encode('utf-8'), filename):
raise OSError('Access denied.')
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)) as fptr: with open(os.path.join(BASEDIR.encode('utf-8'), filename)) as fptr:
content += fptr.read() content += fptr.read()
@ -3503,6 +3538,8 @@ class RequestHandler(BaseHTTPRequestHandler):
try: try:
if filename: if filename:
filename = unquote(filename[0]).encode('utf-8') filename = unquote(filename[0]).encode('utf-8')
if ENFORCE_BASEPATH and not is_safe_path(BASEPATH.encode('utf-8'), filename):
raise OSError('Access denied.')
LOG.info(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:
@ -3520,7 +3557,7 @@ class RequestHandler(BaseHTTPRequestHandler):
self.wfile.write(bytes(content, "utf8")) self.wfile.write(bytes(content, "utf8"))
return return
elif req.path.endswith('/api/listdir'): elif req.path.endswith('/api/listdir'):
content = "" content = {'error': None}
self.send_header('Content-type', 'text/json') self.send_header('Content-type', 'text/json')
self.end_headers() self.end_headers()
dirpath = query.get('path', None) dirpath = query.get('path', None)
@ -3528,6 +3565,8 @@ class RequestHandler(BaseHTTPRequestHandler):
if dirpath: if dirpath:
dirpath = unquote(dirpath[0]).encode('utf-8') dirpath = unquote(dirpath[0]).encode('utf-8')
if os.path.isdir(dirpath): if os.path.isdir(dirpath):
if ENFORCE_BASEPATH and not is_safe_path(BASEPATH.encode('utf-8'), dirpath):
raise OSError('Access denied.')
repo = None repo = None
activebranch = None activebranch = None
dirty = False dirty = False
@ -3547,13 +3586,14 @@ class RequestHandler(BaseHTTPRequestHandler):
'parent': os.path.dirname(os.path.abspath(dirpath)).decode('utf-8'), 'parent': os.path.dirname(os.path.abspath(dirpath)).decode('utf-8'),
'branches': branches, 'branches': branches,
'activebranch': activebranch, 'activebranch': activebranch,
'dirty': dirty 'dirty': dirty,
'error': None
} }
self.wfile.write(bytes(json.dumps(filedata), "utf8")) self.wfile.write(bytes(json.dumps(filedata), "utf8"))
except Exception as err: except Exception as err:
LOG.warning(err) LOG.warning(err)
content = str(err) content['error'] = str(err)
self.wfile.write(bytes(content, "utf8")) self.wfile.write(bytes(json.dumps(content), "utf8"))
return return
elif req.path.endswith('/api/abspath'): elif req.path.endswith('/api/abspath'):
content = "" content = ""
@ -3715,6 +3755,11 @@ class RequestHandler(BaseHTTPRequestHandler):
self.send_header('Content-type', 'text/html') self.send_header('Content-type', 'text/html')
self.end_headers() self.end_headers()
loadfile = query.get('loadfile', [None])[0]
if loadfile is None:
loadfile = 'null'
else:
loadfile = "'%s'" % loadfile
services = "[]" services = "[]"
events = "[]" events = "[]"
states = "[]" states = "[]"
@ -3759,6 +3804,7 @@ class RequestHandler(BaseHTTPRequestHandler):
html = get_html().safe_substitute(services=services, html = get_html().safe_substitute(services=services,
events=events, events=events,
states=states, states=states,
loadfile=loadfile,
current=VERSION, current=VERSION,
versionclass=color, versionclass=color,
separator="\%s" % os.sep if os.sep == "\\" else os.sep, separator="\%s" % os.sep if os.sep == "\\" else os.sep,

102
dev.html
View file

@ -114,7 +114,7 @@
color: #616161 !important; color: #616161 !important;
font-weight: 400; font-weight: 400;
display: inline-block; display: inline-block;
width: 185px; width: 182px;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
cursor: pointer; cursor: pointer;
@ -252,7 +252,7 @@
box-shadow: 0 1px 0 0 #03a9f4 !important; box-shadow: 0 1px 0 0 #03a9f4 !important;
} }
#modal_acekeyboard, #modal_components, #modal_icons { #modal_acekeyboard {
top: auto; top: auto;
width: 96%; width: 96%;
min-height: 96%; min-height: 96%;
@ -488,9 +488,9 @@
height: auto; height: auto;
} }
</style> </style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ace.js" type="text/javascript" charset="utf-8"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ext-modelist.js" type="text/javascript" charset="utf-8"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ext-modelist.js" type="text/javascript" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.9/ext-language_tools.js" type="text/javascript" charset="utf-8"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.3.3/ext-language_tools.js" type="text/javascript" charset="utf-8"></script>
</head> </head>
<body> <body>
<div class="preloader-background"> <div class="preloader-background">
@ -565,10 +565,10 @@
</header> </header>
<main> <main>
<ul id="dropdown_menu" class="dropdown-content z-depth-4"> <ul id="dropdown_menu" class="dropdown-content z-depth-4">
<li><a onclick="localStorage.setItem('new_tab', true);window.open(window.location.href, '_blank');">New tab</a></li> <li><a onclick="localStorage.setItem('new_tab', true);window.open(window.location.origin+window.location.pathname, '_blank');">New tab</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a class="modal-trigger" target="_blank" href="#modal_components">Components</a></li> <li><a target="_blank" href="https://home-assistant.io/components/">Components</a></li>
<li><a class="modal-trigger" target="_blank" href="#modal_icons">Material Icons</a></li> <li><a target="_blank" href="https://materialdesignicons.com/">Material Icons</a></li>
<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 class="modal-trigger" href="#modal_netstat" onclick="get_netstat()">Network status</a></li> <li><a class="modal-trigger" href="#modal_netstat" onclick="get_netstat()">Network status</a></li>
<li><a class="modal-trigger" href="#modal_about">About HASS-Configurator</a></li> <li><a class="modal-trigger" href="#modal_about">About HASS-Configurator</a></li>
@ -584,7 +584,7 @@
<li><a class="modal-trigger" href="#modal_exec_command">Execute shell command</a></li> <li><a class="modal-trigger" href="#modal_exec_command">Execute shell command</a></li>
</ul> </ul>
<ul id="dropdown_menu_mobile" class="dropdown-content z-depth-4"> <ul id="dropdown_menu_mobile" class="dropdown-content z-depth-4">
<li><a onclick="localStorage.setItem('new_tab', true);window.open(window.location.href, '_blank');">New tab</a></li> <li><a onclick="localStorage.setItem('new_tab', true);window.open(window.location.origin+window.location.pathname, '_blank');">New tab</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a target="_blank" href="https://home-assistant.io/help/">Help</a></li> <li><a target="_blank" href="https://home-assistant.io/help/">Help</a></li>
<li><a target="_blank" href="https://home-assistant.io/components/">Components</a></li> <li><a target="_blank" href="https://home-assistant.io/components/">Components</a></li>
@ -613,24 +613,6 @@
<li><a class="modal-trigger" href="#modal_commit" class="nowrap waves-effect">git commit</a></li> <li><a class="modal-trigger" href="#modal_commit" class="nowrap waves-effect">git commit</a></li>
<li><a class="modal-trigger" href="#modal_push" class="nowrap waves-effect">git push</a></li> <li><a class="modal-trigger" href="#modal_push" class="nowrap waves-effect">git push</a></li>
</ul> </ul>
<div id="modal_components" class="modal bottom-sheet modal-fixed-footer">
<div class="modal-content_nopad">
<iframe src="https://home-assistant.io/components/" style="height: 90vh; width: 100vw"> </iframe>
<a target="_blank" href="https://home-assistant.io/components/" class="hide-on-med-and-down modal_btn waves-effect btn-large btn-flat left"><i class="material-icons">launch</i></a>
</div>
<div class="modal-footer">
<a class="modal-action modal-close waves-effect btn-flat Right light-blue-text">Close</a>
</div>
</div>
<div id="modal_icons" class="modal bottom-sheet modal-fixed-footer">
<div class="modal-content_nopad">
<iframe src="https://materialdesignicons.com/" style="height: 90vh; width: 100vw"> </iframe>
<a target="_blank" href="https://materialdesignicons.com/" class="hide-on-med-and-down modal_btn waves-effect btn-large btn-flat left"><i class="material-icons">launch</i></a>
</div>
<div class="modal-footer">
<a class="modal-action modal-close waves-effect btn-flat Right light-blue-text">Close</a>
</div>
</div>
<div id="modal_acekeyboard" class="modal bottom-sheet modal-fixed-footer"> <div id="modal_acekeyboard" class="modal bottom-sheet modal-fixed-footer">
<div class="modal-content centered"> <div class="modal-content centered">
<h4 class="grey-text text-darken-3">Ace Keyboard Shortcuts<i class="mdi mdi-keyboard right grey-text text-darken-3" style="font-size: 2rem;"></i></h4> <h4 class="grey-text text-darken-3">Ace Keyboard Shortcuts<i class="mdi mdi-keyboard right grey-text text-darken-3" style="font-size: 2rem;"></i></h4>
@ -1597,6 +1579,10 @@
<select id="events" onchange="insert(this.value)"></select> <select id="events" onchange="insert(this.value)"></select>
<label>Events</label> <label>Events</label>
</div> </div>
<div class="input-field col s12">
<input type="text" id="entities-search" class="autocomplete" placeholder="sensor.example">
<label>Search entity</label>
</div>
<div class="input-field col s12"> <div class="input-field col s12">
<select id="entities" onchange="insert(this.value)"></select> <select id="entities" onchange="insert(this.value)"></select>
<label>Entities</label> <label>Entities</label>
@ -1696,6 +1682,10 @@
<select id="events_side" onchange="insert(this.value)"></select> <select id="events_side" onchange="insert(this.value)"></select>
<label>Events</label> <label>Events</label>
</div> </div>
<div class="input-field col s12">
<input type="text" id="entities-search_side" class="autocomplete" placeholder="sensor.example">
<label>Search entity</label>
</div>
<div class="input-field col s12"> <div class="input-field col s12">
<select id="entities_side" onchange="insert(this.value)"></select> <select id="entities_side" onchange="insert(this.value)"></select>
<label>Entities</label> <label>Entities</label>
@ -2027,14 +2017,14 @@
<input id="wrap_limit" type="number" onchange="editor.setOption('wrap', parseInt(this.value))" min="1" value="80"> <input id="wrap_limit" type="number" onchange="editor.setOption('wrap', parseInt(this.value))" min="1" value="80">
<label class="active" for="wrap_limit">Wrap Limit</label> <label class="active" for="wrap_limit">Wrap Limit</label>
</div> <a class="waves-effect waves-light btn light-blue" onclick="save_ace_settings()">Save Settings Locally</a> </div> <a class="waves-effect waves-light btn light-blue" onclick="save_ace_settings()">Save Settings Locally</a>
<p class="center col s12"> Ace Editor 1.2.9 </p> <p class="center col s12"> Ace Editor 1.3.3 </p>
</div> </div>
</ul> </ul>
</div> </div>
</main> </main>
<input type="hidden" id="fb_currentfile" value="" /> <input type="hidden" id="fb_currentfile" value="" />
<!-- Scripts --> <!-- Scripts -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.100.2/js/materialize.min.js"></script>
<script> <script>
function ws_connect() { function ws_connect() {
@ -2088,6 +2078,7 @@
} }
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
var init_loadfile = $loadfile;
var global_current_filepath = null; var global_current_filepath = null;
var global_current_filename = null; var global_current_filename = null;
@ -2189,23 +2180,51 @@
$(document).on('click', '.drag-target', function(){$('.button-collapse').sideNav('hide');}) $(document).on('click', '.drag-target', function(){$('.button-collapse').sideNav('hide');})
listdir('.'); listdir('.');
document.getElementById('savePrompt').checked = get_save_prompt(); document.getElementById('savePrompt').checked = get_save_prompt();
var entities_search = new Object();
if (states_list) {
for (var i = 0; i < states_list.length; i++) {
entities_search[states_list[i].attributes.friendly_name + ' (' + states_list[i].entity_id + ')'] = null;
}
}
$('#entities-search').autocomplete({
data: entities_search,
limit: 40,
onAutocomplete: function(val) {
insert(val.split("(")[1].split(")")[0]);
},
minLength: 1,
});
$('#entities-search_side').autocomplete({
data: entities_search,
limit: 40,
onAutocomplete: function(val) {
insert(val.split("(")[1].split(")")[0]);
},
minLength: 1,
});
}); });
</script> </script>
<script type="text/javascript"> <script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
$('.preloader-background').delay(800).fadeOut('slow'); $('.preloader-background').delay(800).fadeOut('slow');
$('.preloader-wrapper').delay(800).fadeOut('slow'); $('.preloader-wrapper').delay(800).fadeOut('slow');
if (!localStorage.getItem("new_tab")) { if (init_loadfile) {
var old_file = localStorage.getItem("current_file"); init_loadfile_name = init_loadfile.split('/').pop();
if (old_file) { loadfile(init_loadfile, init_loadfile_name);
old_file = JSON.parse(old_file);
loadfile(old_file.current_filepath, old_file.current_filename);
}
} }
else { else {
localStorage.removeItem("current_file"); if (!localStorage.getItem("new_tab")) {
var old_file = localStorage.getItem("current_file");
if (old_file) {
old_file = JSON.parse(old_file);
loadfile(old_file.current_filepath, old_file.current_filename);
}
}
else {
localStorage.removeItem("current_file");
}
localStorage.removeItem("new_tab");
} }
localStorage.removeItem("new_tab");
}); });
</script> </script>
<script> <script>
@ -2311,7 +2330,12 @@
function listdir(path) { function listdir(path) {
$.get(encodeURI("api/listdir?path=" + path), function(data) { $.get(encodeURI("api/listdir?path=" + path), function(data) {
renderpath(data); if (!data.error) {
renderpath(data);
}
else {
console.log("Permission denied.");
}
}); });
document.getElementById("slide-out").scrollTop = 0; document.getElementById("slide-out").scrollTop = 0;
} }
@ -3177,7 +3201,7 @@
} }
</script> </script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.10.0/js-yaml.js" type="text/javascript" charset="utf-8"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/js-yaml/3.12.0/js-yaml.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript"> <script type="text/javascript">
var lint_timeout; var lint_timeout;
var lint_status = $('#lint-status'); // speed optimization var lint_status = $('#lint-status'); // speed optimization

View file

@ -2,6 +2,7 @@
"LISTENIP": "0.0.0.0", "LISTENIP": "0.0.0.0",
"LISTENPORT": 3218, "LISTENPORT": 3218,
"BASEPATH": null, "BASEPATH": null,
"ENFORCE_BASEPATH": false,
"SSL_CERTIFICATE": null, "SSL_CERTIFICATE": null,
"SSL_KEY": null, "SSL_KEY": null,
"HASS_API": "http://127.0.0.1:8123/api/", "HASS_API": "http://127.0.0.1:8123/api/",