Add clean MQTT button to dashboard (#139)

This commit is contained in:
Otto Winter 2018-10-04 19:01:02 +02:00 committed by GitHub
parent 12f20fc3cf
commit ef54e33b70
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 87 additions and 4 deletions

View file

@ -451,6 +451,8 @@ def parse_args(argv):
default=6052) default=6052)
dashboard.add_argument("--password", help="The optional password to require for all requests.", dashboard.add_argument("--password", help="The optional password to require for all requests.",
type=str, default='') type=str, default='')
dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.",
action='store_true')
return parser.parse_args(argv[1:]) return parser.parse_args(argv[1:])

View file

@ -117,6 +117,13 @@ class EsphomeyamlValidateHandler(EsphomeyamlCommandWebSocket):
return ["esphomeyaml", config_file, "config"] return ["esphomeyaml", config_file, "config"]
class EsphomeyamlCleanMqttHandler(EsphomeyamlCommandWebSocket):
def build_command(self, message):
js = json.loads(message)
config_file = os.path.join(CONFIG_DIR, js['configuration'])
return ["esphomeyaml", config_file, "clean-mqtt"]
class SerialPortRequestHandler(BaseHandler): class SerialPortRequestHandler(BaseHandler):
def get(self): def get(self):
if not self.is_authenticated(): if not self.is_authenticated():
@ -213,6 +220,7 @@ def make_app(debug=False):
(r"/run", EsphomeyamlRunHandler), (r"/run", EsphomeyamlRunHandler),
(r"/compile", EsphomeyamlCompileHandler), (r"/compile", EsphomeyamlCompileHandler),
(r"/validate", EsphomeyamlValidateHandler), (r"/validate", EsphomeyamlValidateHandler),
(r"/clean-mqtt", EsphomeyamlCleanMqttHandler),
(r"/download.bin", DownloadBinaryRequestHandler), (r"/download.bin", DownloadBinaryRequestHandler),
(r"/serial-ports", SerialPortRequestHandler), (r"/serial-ports", SerialPortRequestHandler),
(r"/wizard.html", WizardRequestHandler), (r"/wizard.html", WizardRequestHandler),
@ -251,6 +259,12 @@ def start_web_server(args):
args.port, CONFIG_DIR) args.port, CONFIG_DIR)
app = make_app(args.verbose) app = make_app(args.verbose)
app.listen(args.port) app.listen(args.port)
if args.open_ui:
import webbrowser
webbrowser.open('localhost:{}'.format(args.port))
try: try:
tornado.ioloop.IOLoop.current().start() tornado.ioloop.IOLoop.current().start()
except KeyboardInterrupt: except KeyboardInterrupt:

View file

@ -159,6 +159,10 @@
margin-right: 24px; margin-right: 24px;
width: 350px; width: 350px;
} }
.dropdown-trigger {
cursor: pointer;
}
</style> </style>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
@ -191,7 +195,7 @@
<main> <main>
<div class="container"> <div class="container">
{% for file, full_path in zip(files, full_path_files) %} {% for i, (file, full_path) in enumerate(zip(files, full_path_files)) %}
<div class="row"> <div class="row">
<div class="col s8 offset-s2 m10 offset-m1 l12"> <div class="col s8 offset-s2 m10 offset-m1 l12">
<div class="card horizontal"> <div class="card horizontal">
@ -200,7 +204,10 @@
</div> </div>
<div class="card-stacked"> <div class="card-stacked">
<div class="card-content"> <div class="card-content">
<span class="card-title">{{ escape(file) }}</span> <span class="card-title">
{{ escape(file) }}
<i class="material-icons right dropdown-trigger" data-target="dropdown-{{ i }}">more_vert</i>
</span>
<p> <p>
Full path: <code class="inlinecode">{{ escape(full_path) }}</code> Full path: <code class="inlinecode">{{ escape(full_path) }}</code>
</p> </p>
@ -211,6 +218,9 @@
<a href="#" class="action-show-logs" data-node="{{ file }}">Show Logs</a> <a href="#" class="action-show-logs" data-node="{{ file }}">Show Logs</a>
<a href="#" class="action-validate" data-node="{{ file }}">Validate</a> <a href="#" class="action-validate" data-node="{{ file }}">Validate</a>
</div> </div>
<ul id="dropdown-{{ i }}" class="dropdown-content">
<li><a href="#" class="action-clean-mqtt" data-node="{{ file }}">Clean MQTT</a></li>
</ul>
</div> </div>
</div> </div>
</div> </div>
@ -462,6 +472,18 @@
</div> </div>
</div> </div>
<div id="modal-clean-mqtt" class="modal modal-fixed-footer">
<div class="modal-content">
<h4>Clean MQTT discovery <code class="inlinecode filename"></code></h4>
<div class="log-container">
<pre class="log"></pre>
</div>
</div>
<div class="modal-footer">
<a class="modal-close waves-effect waves-green btn-flat stop-logs">Stop</a>
</div>
</div>
<a class="btn-floating btn-large ribbon-fab waves-effect waves-light pink accent-2" id="setup-wizard-start"> <a class="btn-floating btn-large ribbon-fab waves-effect waves-light pink accent-2" id="setup-wizard-start">
<i class="material-icons">add</i> <i class="material-icons">add</i>
</a> </a>
@ -539,6 +561,7 @@
if (allEqual) if (allEqual)
return; return;
} }
const hasNewPort = response.length >= ports.length;
ports = response; ports = response;
@ -559,7 +582,7 @@
} }
M.FormSelect.init(portSelect, {}); M.FormSelect.init(portSelect, {});
if (!begin) if (!begin && hasNewPort)
M.toast({html: "Discovered new serial port."}); M.toast({html: "Discovered new serial port."});
}); });
}; };
@ -784,6 +807,48 @@
link.click(); link.click();
}); });
const cleanMqttModalElem = document.getElementById("modal-clean-mqtt");
document.querySelectorAll(".action-clean-mqtt").forEach((btn) => {
btn.addEventListener('click', (e) => {
configuration = e.target.getAttribute('data-node');
const modalInstance = M.Modal.getInstance(cleanMqttModalElem);
const log = cleanMqttModalElem.querySelector(".log");
log.innerHTML = "";
const stopLogsButton = cleanMqttModalElem.querySelector(".stop-logs");
let stopped = false;
stopLogsButton.innerHTML = "Stop";
modalInstance.open();
const filenameField = cleanMqttModalElem.querySelector('.filename');
filenameField.innerHTML = configuration;
const logSocket = new WebSocket(wsUrl + "/clean-mqtt");
logSocket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.event === "line") {
const msg = data.data;
log.innerHTML += colorReplace(msg);
} else if (data.event === "exit") {
stopLogsButton.innerHTML = "Close";
stopped = true;
}
});
logSocket.addEventListener('open', () => {
const msg = JSON.stringify({configuration: configuration});
logSocket.send(msg);
});
logSocket.addEventListener('close', () => {
if (!stopped) {
M.toast({html: 'Terminated process.'});
}
});
modalInstance.options.onCloseStart = () => {
logSocket.close();
};
});
});
const modalSetupElem = document.getElementById("modal-wizard"); const modalSetupElem = document.getElementById("modal-wizard");
const setupWizardStart = document.getElementById('setup-wizard-start'); const setupWizardStart = document.getElementById('setup-wizard-start');
const startWizard = () => { const startWizard = () => {

View file

@ -83,7 +83,9 @@ def clear_topic(config, topic, username=None, password=None, client_id=None):
discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, u'homeassistant') discovery_prefix = config[CONF_MQTT].get(CONF_DISCOVERY_PREFIX, u'homeassistant')
name = config[CONF_ESPHOMEYAML][CONF_NAME] name = config[CONF_ESPHOMEYAML][CONF_NAME]
topic = u'{}/+/{}/#'.format(discovery_prefix, name) topic = u'{}/+/{}/#'.format(discovery_prefix, name)
_LOGGER.info(u"Clearing messages from %s", topic) _LOGGER.info(u"Clearing messages from '%s'", topic)
_LOGGER.info(u"Please close this window when no more messages appear and the "
u"MQTT topic has been cleared of retained messages.")
def on_message(client, userdata, msg): def on_message(client, userdata, msg):
if not msg.payload or not msg.retain: if not msg.payload or not msg.retain: