syslog and removed footable

This commit is contained in:
proddy
2019-11-20 21:26:43 +01:00
parent bcf251452e
commit e26aaf3d1a
13 changed files with 176 additions and 173 deletions

View File

@@ -51,6 +51,7 @@ lib_deps =
ArduinoJson@6.13.0
ESPAsyncUDP
ESPAsyncTCP@1.2.2
https://github.com/nomis/mcu-uuid-syslog.git
upload_speed = 921600
monitor_speed = 115200

View File

@@ -46,7 +46,8 @@ MyESP::MyESP() {
_ota_post_callback_f = nullptr;
_load_average = 100; // calculated load average
_general_serial = true; // serial is set to on as default
_general_log_events = true; // all logs are written to an event log in SPIFFS
_general_log_events = true; // all logs are sent to syslog
_general_log_ip = nullptr;
_have_ntp_time = false;
// telnet
@@ -830,6 +831,11 @@ void MyESP::_printSetCommands() {
myDebug_P(PSTR(" ntp_timezone=%d"), _ntp_timezone);
myDebug_P(PSTR(" log_events=%s"), (_general_log_events) ? "on" : "off");
if (_hasValue(_general_log_ip)) {
myDebug_P(PSTR(" log_ip=%s"), _general_log_ip);
} else {
myDebug_P(PSTR(" log_ip="));
}
// print any custom settings
if (_fs_setlist_callback_f) {
@@ -924,6 +930,8 @@ bool MyESP::_changeSetting(uint8_t wc, const char * setting, const char * value)
save_config = fs_setSettingValue(&_ntp_timezone, value, NTP_TIMEZONE_DEFAULT);
} else if (strcmp(setting, "log_events") == 0) {
save_config = fs_setSettingValue(&_general_log_events, value, false);
} else if (strcmp(setting, "log_ip") == 0) {
save_config = fs_setSettingValue(&_general_log_ip, value, "");
} else {
// finally check for any custom commands
if (_fs_setlist_callback_f) {
@@ -1722,6 +1730,7 @@ bool MyESP::_fs_loadConfig() {
_ws->setAuthentication("admin", _general_password);
_general_hostname = strdup(general["hostname"]);
_general_log_events = general["log_events"];
_general_log_ip = strdup(general["log_ip"] | "");
// serial is only on when booting
#ifdef FORCE_SERIAL
@@ -1871,9 +1880,7 @@ bool MyESP::fs_saveCustomConfig(JsonObject root) {
configFile.close();
if (n) {
if (_general_log_events) {
_writeLogEvent("INFO", "system", "Custom config stored in the SPIFFS", "");
}
_writeLogEvent(MYESP_SYSLOG_INFO, "Custom config stored in the SPIFFS");
ok = true;
}
}
@@ -1906,9 +1913,7 @@ bool MyESP::fs_saveConfig(JsonObject root) {
configFile.close();
if (n) {
if (_general_log_events) {
_writeLogEvent("INFO", "system", "System config stored in the SPIFFS", "");
}
_writeLogEvent(MYESP_SYSLOG_INFO, "System config stored in the SPIFFS");
ok = true;
}
@@ -1945,6 +1950,7 @@ bool MyESP::_fs_writeConfig() {
general["serial"] = _general_serial;
general["hostname"] = _general_hostname;
general["log_events"] = _general_log_events;
general["log_ip"] = _general_log_ip;
general["version"] = _app_version;
JsonObject mqtt = doc.createNestedObject("mqtt");
@@ -2013,22 +2019,20 @@ void MyESP::_fs_setup() {
SPIFFS.remove(MYESP_OLD_CONFIG_FILE);
myDebug_P(PSTR("[FS] Removed old config settings"));
}
// see if old file exists and delete it
if (SPIFFS.exists(MYESP_OLD_CONFIG_FILE)) {
SPIFFS.remove(MYESP_OLD_CONFIG_FILE);
myDebug_P(PSTR("[FS] Removed old config settings"));
if (SPIFFS.exists(MYESP_OLD_EVENTLOG_FILE)) {
SPIFFS.remove(MYESP_OLD_EVENTLOG_FILE);
myDebug_P(PSTR("[FS] Removed old event log"));
}
// load the main system config file if we can. Otherwise create it and expect user to configure in web interface
if (!_fs_loadConfig()) {
myDebug_P(PSTR("[FS] Creating a new system config"));
_fs_writeConfig(); // create the initial config file
_fs_writeConfig();
}
// load system and custom config
if (!_fs_loadCustomConfig()) {
_fs_createCustomConfig(); // create the initial config file
_fs_createCustomConfig();
}
if (_ota_post_callback_f) {
@@ -2253,11 +2257,23 @@ void MyESP::crashInfo() {
#endif
// write a log entry to SysLog via UDP
// assumes we have "log_events" on
void MyESP::_writeLogEvent(const char * type, const char * src, const char * desc, const char * data) {
// TODO: finish function
void MyESP::_writeLogEvent(const uint8_t type, const char * msg) {
if (!_general_log_events) {
return;
}
static uuid::log::Logger logger{F("eventlog")};
// uuid::log::INFO
// uuid::log::ERROR
if (type == MYESP_SYSLOG_INFO) {
logger.info(msg);
} else if (type == MYESP_SYSLOG_ERROR) {
logger.err(msg);
}
#ifdef MYESP_DEBUG
Serial.printf("%s: %s\n", type, desc);
Serial.printf("%d: %s\n", type, msg);
#endif
}
@@ -2569,10 +2585,10 @@ void MyESP::_webserver_setup() {
}
if (!index) {
ETS_UART_INTR_DISABLE(); // disable all UART interrupts to be safe
_writeLogEvent("INFO", "system", "Firmware update started", "");
//_writeLogEvent(MYESP_SYSLOG_INFO, "Firmware update started");
Update.runAsync(true);
if (!Update.begin((ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000)) {
_writeLogEvent("ERRO", "system", "Not enough space to update", "");
//_writeLogEvent(MYESP_SYSLOG_ERROR, "Not enough space to update");
#ifdef MYESP_DEBUG
Update.printError(Serial);
#endif
@@ -2580,7 +2596,7 @@ void MyESP::_webserver_setup() {
}
if (!Update.hasError()) {
if (Update.write(data, len) != len) {
_writeLogEvent("ERRO", "system", "Writing to flash failed", "");
//_writeLogEvent(MYESP_SYSLOG_ERROR, "Writing to flash failed");
#ifdef MYESP_DEBUG
Update.printError(Serial);
#endif
@@ -2588,10 +2604,10 @@ void MyESP::_webserver_setup() {
}
if (final) {
if (Update.end(true)) {
_writeLogEvent("INFO", "system", "Firmware update finished", "");
//_writeLogEvent(MYESP_SYSLOG_INFO, "Firmware update finished");
_shouldRestart = !Update.hasError();
} else {
_writeLogEvent("ERRO", "system", "Firmware update failed", "");
//_writeLogEvent(MYESP_SYSLOG_ERROR, "Firmware update failed");
#ifdef MYESP_DEBUG
Update.printError(Serial);
#endif
@@ -2765,9 +2781,7 @@ void MyESP::_bootupSequence() {
if (boot_status == MYESP_BOOTSTATUS_BOOTED) {
if ((_ntp_enabled) && (now() > 10000) && !_have_ntp_time) {
_have_ntp_time = true;
if (_general_log_events) {
_writeLogEvent("INFO", "system", "System booted", "");
}
_writeLogEvent(MYESP_SYSLOG_INFO, "System booted");
}
return;
}
@@ -2797,11 +2811,30 @@ void MyESP::_bootupSequence() {
// write a log message if we're not using NTP, otherwise wait for the internet time to arrive
if (!_ntp_enabled) {
if (_general_log_events) {
_writeLogEvent("INFO", "system", "System booted", "");
_writeLogEvent(MYESP_SYSLOG_INFO, "System booted");
}
}
}
// set up SysLog
void MyESP::_syslog_setup() {
// if not enabled or IP is empty, don't bother
if ( (!_hasValue(_general_log_ip)) || (!_general_log_events) ) {
return;
}
static uuid::log::Logger logger{F("setup")};
syslog.start();
syslog.hostname(_general_hostname);
syslog.log_level(uuid::log::DEBUG); // default log level
syslog.mark_interval(3600);
IPAddress syslog_ip;
syslog.destination(syslog_ip.fromString(_general_log_ip));
myDebug_P(PSTR("[SYSLOG] System event logging enabled"));
}
// setup MyESP
@@ -2815,7 +2848,6 @@ void MyESP::begin(const char * app_hostname, const char * app_name, const char *
strlcpy(s, app_url_api, sizeof(s));
strlcat(s, "/releases/latest", sizeof(s)); // append "/releases/latest"
_app_updateurl = strdup(s);
strlcpy(s, app_url_api, sizeof(s));
strlcat(s, "/releases/tags/travis-dev-build", sizeof(s)); // append "/releases/tags/travis-dev-build"
_app_updateurl_dev = strdup(s);
@@ -2825,6 +2857,8 @@ void MyESP::begin(const char * app_hostname, const char * app_name, const char *
// print a welcome message
myDebug_P(PSTR("\n\n* %s version %s"), _app_name, _app_version);
_syslog_setup(); // SysLog
// set up onboard LED
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
@@ -2865,6 +2899,10 @@ void MyESP::loop() {
_mqttConnect(); // MQTT
// SysLog
uuid::loop();
syslog.loop();
if (_timerequest) {
_timerequest = false;
_sendTime();
@@ -2880,9 +2918,7 @@ void MyESP::loop() {
}
if (_shouldRestart) {
if (_general_log_events) {
_writeLogEvent("INFO", "system", "System is restarting", "");
}
_writeLogEvent(MYESP_SYSLOG_INFO, "System is restarting");
myDebug("[SYSTEM] Restarting...");
_deferredReset(500, CUSTOM_RESET_TERMINAL);
ESP.restart();

View File

@@ -9,7 +9,7 @@
#ifndef MyESP_h
#define MyESP_h
#define MYESP_VERSION "1.2.20"
#define MYESP_VERSION "1.2.21"
#include <ArduinoJson.h>
#include <ArduinoOTA.h>
@@ -18,6 +18,14 @@
#include <FS.h>
#include <JustWifi.h>
// SysLog
#include <uuid/common.h>
#include <uuid/log.h>
#include <uuid/syslog.h>
static uuid::syslog::SyslogService syslog;
enum MYESP_SYSLOG_LEVEL : uint8_t { MYESP_SYSLOG_INFO, MYESP_SYSLOG_ERROR };
// local libraries
#include "Ntp.h"
#include "TelnetSpy.h" // modified from https://github.com/yasheena/telnetspy
@@ -53,7 +61,7 @@ extern struct rst_info resetInfo;
#define MYESP_CONFIG_FILE "/myesp.json"
#define MYESP_CUSTOMCONFIG_FILE "/customconfig.json"
#define MYESP_EVENTLOG_FILE "/eventlog.json" // depreciated
#define MYESP_OLD_EVENTLOG_FILE "/eventlog.json" // depreciated
#define MYESP_OLD_CONFIG_FILE "/config.json" // depreciated
#define MYESP_HTTP_USERNAME "admin" // HTTP username
@@ -145,10 +153,9 @@ PROGMEM const char * const custom_reset_string[] = {custom_reset_hardware, cus
#define CUSTOM_RESET_FACTORY 5 // Factory reset
#define CUSTOM_RESET_MAX 5
// SPIFFS
// SPIFFS - max allocation is 1000 KB
// https://arduinojson.org/v6/assistant/
#define MYESP_SPIFFS_MAXSIZE_CONFIG 800 // max size for a config file
#define MYESP_SPIFFS_MAXSIZE_EVENTLOG 20000 // max size for the eventlog in bytes
#define MYESP_SPIFFS_MAXSIZE_CONFIG 999 // max size for a config file
// CRASH
/**
@@ -266,9 +273,6 @@ class MyESP {
MyESP();
~MyESP();
// write event called from within lambda class
static void _writeLogEvent(const char * type, const char * src, const char * desc, const char * data);
// wifi
void setWIFICallback(void (*callback)());
void setWIFI(wifi_callback_f callback);
@@ -293,6 +297,9 @@ class MyESP {
bool getUseSerial();
void setUseSerial(bool toggle);
// syslog
void _writeLogEvent(const uint8_t type, const char * msg);
// FS
void setSettings(fs_loadsave_callback_f loadsave, fs_setlist_callback_f setlist, bool useSerial = true);
bool fs_saveConfig(JsonObject root);
@@ -383,7 +390,7 @@ class MyESP {
// crash
void _eeprom_setup();
// telnet & debug
// telnet
TelnetSpy SerialAndTelnet;
void _telnetConnected();
void _telnetDisconnected();
@@ -397,6 +404,9 @@ class MyESP {
telnet_callback_f _telnet_callback_f; // callback for connect/disconnect
bool _changeSetting(uint8_t wc, const char * setting, const char * value);
// syslog
void _syslog_setup();
// fs and settings
void _fs_setup();
bool _fs_loadConfig();
@@ -423,6 +433,7 @@ class MyESP {
bool _suspendOutput;
bool _general_serial;
bool _general_log_events;
char * _general_log_ip;
char * _buildTime;
bool _timerequest;
bool _formatreq;

View File

@@ -259,4 +259,5 @@
<div class="row form-group" style="text-align: center;">
<button onclick="refreshCustomStatus()" class="btn btn-info">Refresh</button>
</div>
<br>
</div>

View File

@@ -427,7 +427,7 @@ _EMS_SYS_LOGGING ems_getLogging() {
}
void ems_setLogging(_EMS_SYS_LOGGING loglevel, uint16_t type_id) {
if (loglevel <= EMS_SYS_LOGGING_WATCH) {
if (loglevel <= EMS_SYS_LOGGING_JABBER) {
EMS_Sys_Status.emsLogging = loglevel;
if (loglevel == EMS_SYS_LOGGING_NONE) {

View File

@@ -1 +1 @@
#define APP_VERSION "1.9.4b15"
#define APP_VERSION "1.9.4b16"

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -192,7 +192,7 @@
<div class="modal-body">
<div>
<div>
<button id="updateb" onclick="switchfirmware()" type="submit" class="btn btn-success btn-sm pull-right">Official Release</button>
<button id="updateb" onclick="switchfirmware()" type="submit" class="btn btn-success btn-sm pull-right">Release</button>
</div>
<div id="onlineupdate">

View File

@@ -100,7 +100,6 @@
</span>
<br>
</div>
<div class="row form-group">
<label class="col-xs-3">Serial Port<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
@@ -115,7 +114,6 @@
</div>
<br>
</div>
<div class="row form-group">
<label class="col-xs-3">Event Logging<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
@@ -128,16 +126,26 @@
<input type="radio" value="0" name="logeventsenabled" checked>Disabled</label>
</form>
</div>
</div>
<div class="row form-group">
<label class="col-xs-3">Event Log Server IP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
data-content="IP address of the SysLog server"></i></label>
<span class="col-xs-9">
<input class="form-control input-sm" value="" style="display:inline;max-width:185px" id="log_ip"
type="text">
</span>
</div>
<br>
<br>
<div class="col-xs-9 col-md-8">
<button onclick="savegeneral()" class="btn btn-primary btn-sm pull-right">Save</button>
</div>
</div>
<div style="clear:both;">
</div>
<div style="clear:both;">
<br>
<br>
</div>
</div>
</div>
<br>
</div>
@@ -304,7 +312,8 @@
</span>
<br><br>
<h6 style="margin-left: 10px;" class="text-muted">If you're not using DHCP and want a fixed IP address enter the values below:</h6>
<h6 style="margin-left: 10px;" class="text-muted">If you're not using DHCP and want a fixed IP address enter the
values below:</h6>
<br>
<label class="col-xs-3">Static IP<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
@@ -494,26 +503,22 @@
</tr>
</table>
</div>
<div class="panel panel-default table-responsive">
<table class="table">
<caption>MQTT</caption>
<tr>
<td>
<div id="mqttconnected"></div>
</td>
<td>
<div id="mqttheartbeat"></div>
</td>
</tr>
</table>
<div class="panel-heading">
<div class="panel-title" id="mqttloghdr"></div>
</div>
<div>
<table id="mqttlogtable" class="table" data-paging="false" data-filtering="false"
data-sorting="false" data-editing="false" data-state="true"></table>
</div>
<div class="panel panel-default table-responsive">
<table class="table table-hover table-striped table-condensed" border=1>
<caption>MQTT&nbsp;&nbsp;<div id="mqttconnected"></div>&nbsp;<div id="mqttheartbeat"></div>
</caption>
<thead>
<tr>
<th>Time</th>
<th>Topic<i style="margin-left: 10px;" class="glyphicon glyphicon-info-sign"
aria-hidden="true" data-toggle="popover" data-trigger="hover" data-placement="right"
id="mqttloghdr"></i></th>
<th>Payload</th>
</tr>
</thead>
<tbody id="mqttlog"></tbody>
</table>
</div>
</div>
</div>

View File

@@ -3,7 +3,6 @@ var version = "";
var websock = null;
var wsUri = "ws://" + window.location.host + "/ws";
var ntpSeconds;
var data = [];
var ajaxobj;
var custom_config = {};
@@ -11,8 +10,6 @@ var custom_config = {};
var xDown = null;
var yDown = null;
var page = 1;
var haspages;
var file = {};
var backupstarted = false;
var updateurl = "";
@@ -122,6 +119,8 @@ function savegeneral() {
config.general.log_events = true;
}
config.general.log_ip = document.getElementById("log_ip").value;
saveconfig();
}
@@ -287,6 +286,9 @@ function listgeneral() {
if (config.general.log_events) {
$("input[name=\"logeventsenabled\"][value=\"1\"]").prop("checked", true);
}
document.getElementById("log_ip").value = config.general.log_ip;
}
function listmqtt() {
@@ -346,20 +348,6 @@ function isVisible(e) {
return !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length);
}
function getnextpage(mode) {
if (!backupstarted) {
document.getElementById("loadpages").innerHTML = "Loading " + page + "/" + haspages;
}
if (page < haspages) {
page = page + 1;
var commandtosend = {};
commandtosend.command = mode;
commandtosend.page = page;
websock.send(JSON.stringify(commandtosend));
}
}
function colorStatusbar(ref) {
var percentage = ref.style.width.slice(0, -1);
if (percentage > 50) { ref.className = "progress-bar progress-bar-success"; } else if (percentage > 25) { ref.className = "progress-bar progress-bar-warning"; } else { ref.class = "progress-bar progress-bar-danger"; }
@@ -403,8 +391,40 @@ function listStats() {
document.getElementById("mqttheartbeat").className = "label label-primary";
}
document.getElementById("mqttloghdr").innerHTML = "MQTT Publish Log: (topics are prefixed with <b>" + ajaxobj.mqttloghdr + "</b>)";
document.getElementById("mqttloghdr").setAttribute('data-content', "Topics are prefixed with " + ajaxobj.mqttloghdr);
var mtable = document.getElementById("mqttlog");
var obj = ajaxobj.mqttlog;
var tr, td;
for (var i = 0; i < obj.length; i++) {
tr = document.createElement("tr");
td = document.createElement("td");
if (obj[i].time < 1563300000) {
td.innerHTML = "(" + obj[i].time + ")";
} else {
var vuepoch = new Date(obj[i].time * 1000);
td.innerHTML = vuepoch.getUTCFullYear() +
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
"-" + twoDigits(vuepoch.getUTCDate()) +
" " + twoDigits(vuepoch.getUTCHours()) +
":" + twoDigits(vuepoch.getUTCMinutes()) +
":" + twoDigits(vuepoch.getUTCSeconds());
}
tr.appendChild(td);
td = document.createElement("td");
td.innerHTML = obj[i].topic
tr.appendChild(td);
td = document.createElement("td");
td.innerHTML = obj[i].payload
tr.appendChild(td);
mtable.appendChild(tr);
}
}
function getContent(contentname) {
@@ -430,19 +450,6 @@ function getContent(contentname) {
case "#networkcontent":
listnetwork();
break;
case "#eventcontent":
page = 1;
data = [];
getEvents();
if (config.general.log_events) {
document.getElementById("logevents").style.display = "none";
} else {
document.getElementById("logevents").style.display = "block";
}
break;
case "#customcontent":
listcustom();
break;
@@ -562,60 +569,12 @@ function twoDigits(value) {
return value;
}
function initMQTTLogTable() {
var newlist = [];
for (var i = 0; i < ajaxobj.mqttlog.length; i++) {
var data = JSON.stringify(ajaxobj.mqttlog[i]);
newlist[i] = {};
newlist[i].options = {};
newlist[i].value = {};
newlist[i].value = JSON.parse(data);
newlist[i].options.classes = "warning";
newlist[i].options.style = "color: blue";
}
jQuery(function ($) {
window.FooTable.init("#mqttlogtable", {
columns: [{
"name": "time",
"title": "Last Published",
"style": { "min-width": "160px" },
"parser": function (value) {
if (value < 1563300000) {
return "(" + value + ")";
} else {
var vuepoch = new Date(value * 1000);
var formatted = vuepoch.getUTCFullYear() +
"-" + twoDigits(vuepoch.getUTCMonth() + 1) +
"-" + twoDigits(vuepoch.getUTCDate()) +
" " + twoDigits(vuepoch.getUTCHours()) +
":" + twoDigits(vuepoch.getUTCMinutes()) +
":" + twoDigits(vuepoch.getUTCSeconds());
return formatted;
}
},
"breakpoints": "xs sm"
},
{
"name": "topic",
"title": "Topic",
},
{
"name": "payload",
"title": "Payload",
},
],
rows: newlist
});
});
}
function socketMessageListener(evt) {
var obj = JSON.parse(evt.data);
if (obj.hasOwnProperty("command")) {
switch (obj.command) {
case "status":
ajaxobj = obj;
initMQTTLogTable();
getContent("#statuscontent");
break;
case "custom_settings":
@@ -757,13 +716,13 @@ function login() {
}
}
function switchfirmware() {
function getfirmware() {
if (use_beta_firmware) {
use_beta_firmware = false;
document.getElementById("updateb").innerHTML = "Official Release";
document.getElementById("updateb").innerHTML = "Switch to Development build";
} else {
use_beta_firmware = true;
document.getElementById("updateb").innerHTML = "Development Build";
document.getElementById("updateb").innerHTML = "Switch to Stable release";
}
getLatestReleaseInfo();
}
@@ -855,7 +814,7 @@ $("#backup").click(function () { getContent("#backupcontent"); return false; });
$("#reset").click(function () { $("#destroy").modal("show"); return false; });
$("#restart").click(function () { $("#reboot").modal("show"); return false; });
$(".noimp").on("click", function () { $("#noimp").modal("show"); });
$("#update").on("shown.bs.modal", function (e) { getLatestReleaseInfo(); });
$("#update").on("shown.bs.modal", function (e) { getfirmware(); });
document.addEventListener("touchstart", handleTouchStart, false);
document.addEventListener("touchmove", handleTouchMove, false);

View File

@@ -100,7 +100,7 @@ gulp.task('myespjs', function () {
});
gulp.task('requiredjs', function () {
return gulp.src(['../../src/websrc/3rdparty/js/jquery-1.12.4.min.js', '../../src/websrc/3rdparty/js/bootstrap-3.4.1.min.js', '../../src/websrc/3rdparty/js/footable-3.1.6.min.js'])
return gulp.src(['../../src/websrc/3rdparty/js/jquery-1.12.4.min.js', '../../src/websrc/3rdparty/js/bootstrap-3.4.1.min.js'])
.pipe(concat({
path: 'required.js',
stat: {
@@ -117,7 +117,7 @@ gulp.task('requiredjs', function () {
gulp.task('requiredcss', function () {
return gulp.src(['../../src/websrc/3rdparty/css/bootstrap-3.4.1.min.css', '../../src/websrc/3rdparty/css/footable.bootstrap-3.1.6.min.css', '../../src/websrc/3rdparty/css/sidebar.css'])
return gulp.src(['../../src/websrc/3rdparty/css/bootstrap-3.4.1.min.css', '../../src/websrc/3rdparty/css/sidebar.css'])
.pipe(concat({
path: 'required.css',
stat: {

View File

@@ -64,7 +64,8 @@ var configfile = {
"password": "admin",
"serial": true,
"version": "1.0.0",
"log_events": true
"log_events": false,
"log_ip": "10.11.12.13"
},
"mqtt": {
"enabled": false,