Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions pio-scripts/inject_syslog_ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# pio-scripts/inject_syslog_ui.py
import os, shutil, sys
from SCons.Script import Import

Import("env")

# detect full vs. partial compile
is_full_build = env.get("PIOENV") is not None

# Track the state between builds
def get_previous_syslog_state(project_dir):
state_file = os.path.join(project_dir, "wled00/data/.syslog_state")
if os.path.exists(state_file):
with open(state_file, 'r') as f:
return f.read().strip() == "1"
return None # None means no previous state recorded

def set_syslog_state(project_dir, enabled):
state_file = os.path.join(project_dir, "wled00/data/.syslog_state")
with open(state_file, 'w') as f:
f.write("1" if enabled else "0")

# This is the HTML we want to inject
SYSLOG_HTML = """
<h3>Syslog</h3>
<div id="NoSyslog" class="hide">
<em class="warn">This firmware build does not support Syslog interface.<br></em>
</div>
<div id="Syslog">
Enable Syslog: <input type="checkbox" name="SL_en"><br>
Host: <input type="text" name="SL_host" maxlength="32"><br>
Port: <input type="number" name="SL_port" min="1" max="65535" value="%SL_port%"><br>
Protocol:
<select name="SL_proto">
<option value="0">BSD (RFC3164)</option>
<option value="1">RFC5424</option>
<option value="2">Raw</option>
</select><br>
Facility:
<select name="SL_fac">
<option value="0">KERN</option>
<option value="1">USER</option>
<option value="2">MAIL</option>
<option value="3">DAEMON</option>
<option value="4">AUTH</option>
<option value="5">SYSLOG</option>
<option value="6">LPR</option>
<option value="7">NEWS</option>
<option value="8">UUCP</option>
<option value="9">CRON</option>
<option value="10">AUTHPRIV</option>
<option value="11">FTP</option>
<option value="12">NTP</option>
<option value="13">LOG_AUDIT</option>
<option value="14">LOG_ALERT</option>
<option value="15">CLOCK_DAEMON</option>
<option value="16">LOCAL0</option>
<option value="17">LOCAL1</option>
<option value="18">LOCAL2</option>
<option value="19">LOCAL3</option>
<option value="20">LOCAL4</option>
<option value="21">LOCAL5</option>
<option value="22">LOCAL6</option>
<option value="23">LOCAL7</option>
</select><br>
Severity:
<select name="SL_sev">
<option value="0">EMERG</option>
<option value="1">ALERT</option>
<option value="2">CRIT</option>
<option value="3">ERR</option>
<option value="4">WARNING</option>
<option value="5">NOTICE</option>
<option value="6">INFO</option>
<option value="7">DEBUG</option>
</select><br>
</div>
"""

def inject_syslog_ui(source, target, env):
print("\033[44m==== inject_syslog_ui.py (PRE BUILD) ====\033[0m")
if not is_full_build:
print("\033[43mNot a full build, skipping Syslog UI operations.\033[0m")
return

# Check for the define in BUILD_FLAGS
build_flags = env.get("BUILD_FLAGS", "")
if isinstance(build_flags, list):
build_flags = " ".join(build_flags)
has_syslog = ("-D WLED_ENABLE_SYSLOG" in build_flags or "-DWLED_ENABLE_SYSLOG" in build_flags)

project_dir = env.subst("$PROJECT_DIR")
html_path = os.path.join(project_dir, "wled00/data/settings_sync.htm")
bak = html_path + ".backup"

# Detect state change → touch to force rebuild
prev = get_previous_syslog_state(project_dir)
if prev is not None and prev != has_syslog:
print(f"\033[43mSYSLOG state changed from {prev} to {has_syslog}, forcing UI rebuild.\033[0m")
if os.path.exists(html_path):
with open(html_path, 'a'):
os.utime(html_path, None)

set_syslog_state(project_dir, has_syslog)

if not has_syslog:
print("\033[43mWLED_ENABLE_SYSLOG not defined, skipping injection.\033[0m")
# restore if backup exists
if os.path.exists(bak):
print("Restoring original file from backup...")
shutil.copy2(bak, html_path)
os.remove(bak)
return

# backup + inject only once
if not os.path.exists(bak):
print("Backing up and injecting Syslog UI...")
shutil.copyfile(html_path, bak)
try:
original = open(html_path, 'r', encoding='utf8').read()
modified = original

# replace existing section if present
if '<!-- SYSLOG-START -->' in modified and '<!-- SYSLOG-END -->' in modified:
start = modified.index('<!-- SYSLOG-START -->')
end = modified.index('<!-- SYSLOG-END -->') + len('<!-- SYSLOG-END -->')
modified = (
modified[:start]
+ '<!-- SYSLOG-START -->\n' + SYSLOG_HTML + '\n<!-- SYSLOG-END -->'
+ modified[end:]
)
else:
# insert before last <hr>
idx = modified.rfind('<hr>')
if idx == -1:
print("\033[41mCould not find <hr> to insert Syslog UI!\033[0m")
return
modified = (
modified[:idx]
+ '<!-- SYSLOG-START -->\n' + SYSLOG_HTML + '\n<!-- SYSLOG-END -->\n'
+ modified[idx:]
)

with open(html_path, 'w', encoding='utf8') as f:
f.write(modified)
print("\033[42mSyslog UI injected successfully!\033[0m")
except Exception as e:
print(f"\033[41mError during injection: {e}\033[0m")
else:
print("Backup exists; assume already injected.")

def restore_syslog_ui(source, target, env):
print("\033[44m==== inject_syslog_ui.py (POST BUILD) ====\033[0m")
project_dir = env.subst("$PROJECT_DIR")
html_path = os.path.join(project_dir, "wled00/data/settings_sync.htm")
bak = html_path + ".backup"

# restore only if backup file is present
if os.path.exists(bak):
print("Restoring original file from backup...")
if os.path.exists(html_path):
os.chmod(html_path, 0o644)
shutil.copy2(bak, html_path)
os.remove(bak)

# always register the post-action on checkprogsize so it runs every build
env.AddPostAction("checkprogsize", restore_syslog_ui)

# only inject on full build
if is_full_build:
inject_syslog_ui(None, None, env)
1 change: 1 addition & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ extra_scripts =
post:pio-scripts/strip-floats.py
pre:pio-scripts/user_config_copy.py
pre:pio-scripts/load_usermods.py
pre:pio-scripts/inject_syslog_ui.py
pre:pio-scripts/build_ui.py
; post:pio-scripts/obj-dump.py ;; convenience script to create a disassembly dump of the firmware (hardcore debugging)

Expand Down
5 changes: 4 additions & 1 deletion wled00/bus_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ make_unique(Args&&... args)
#endif

// enable additional debug output
#if defined(WLED_DEBUG_HOST)
#if defined(WLED_ENABLE_SYSLOG)
#include "syslog.h"
#define DEBUGOUT Syslog
#elif defined(WLED_DEBUG_HOST)
#include "net_debug.h"
#define DEBUGOUT NetDebug
#else
Expand Down
20 changes: 20 additions & 0 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,16 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(hueIP[i], if_hue_ip[i]);
#endif

#ifdef WLED_ENABLE_SYSLOG
JsonObject if_syslog = interfaces["syslog"];
CJSON(syslogEnabled, if_syslog["en"]);
getStringFromJson(syslogHost, if_syslog[F("host")], 33);
CJSON(syslogPort, if_syslog["port"]);
CJSON(syslogProtocol, if_syslog["proto"]);
CJSON(syslogFacility, if_syslog["fac"]);
CJSON(syslogSeverity, if_syslog["sev"]);
#endif

JsonObject if_ntp = interfaces[F("ntp")];
CJSON(ntpEnabled, if_ntp["en"]);
getStringFromJson(ntpServerName, if_ntp[F("host")], 33); // "1.wled.pool.ntp.org"
Expand Down Expand Up @@ -1051,6 +1061,16 @@ void serializeConfig(JsonObject root) {
}
#endif

#ifdef WLED_ENABLE_SYSLOG
JsonObject if_syslog = interfaces.createNestedObject("syslog");
if_syslog["en"] = syslogEnabled;
if_syslog["host"] = syslogHost;
if_syslog["port"] = syslogPort;
if_syslog["proto"] = syslogProtocol;
if_syslog["fac"] = syslogFacility;
if_syslog["sev"] = syslogSeverity;
#endif

JsonObject if_ntp = interfaces.createNestedObject("ntp");
if_ntp["en"] = ntpEnabled;
if_ntp[F("host")] = ntpServerName;
Expand Down
1 change: 1 addition & 0 deletions wled00/data/settings_sync.htm
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ <h3>Serial</h3>
</select><br>
<i>Keep at 115200 to use Improv. Some boards may not support high rates.</i>
</div>
<!-- SYSLOG-START --><!-- SYSLOG-END -->
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
Expand Down
8 changes: 7 additions & 1 deletion wled00/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,10 @@ bool deserializeState(JsonObject root, byte callMode, byte presetId)
bool stateResponse = root[F("v")] | false;

#if defined(WLED_DEBUG) && defined(WLED_DEBUG_HOST)
netDebugEnabled = root[F("debug")] | netDebugEnabled;
netDebugEnabled = root[F("debug")] | netDebugEnabled;
#elif defined(WLED_DEBUG) && defined(WLED_ENABLE_SYSLOG)
syslogEnabled = root[F("debug")] | syslogEnabled;
DEBUG_PRINTF_P(PSTR("Syslog: %s\n"), syslogEnabled ? PSTR("ENABLED") : PSTR("DISABLED") );
#endif

bool onBefore = bri;
Expand Down Expand Up @@ -781,6 +784,9 @@ void serializeInfo(JsonObject root)
#ifdef WLED_DEBUG_HOST
os |= 0x0100;
if (!netDebugEnabled) os &= ~0x0080;
#elif defined(WLED_ENABLE_SYSLOG)
os |= 0x0100;
if (!syslogEnabled) os &= ~0x0080;
#endif
#endif
#ifndef WLED_DISABLE_ALEXA
Expand Down
22 changes: 22 additions & 0 deletions wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,28 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
t = request->arg(F("BD")).toInt();
if (t >= 96 && t <= 15000) serialBaud = t;
updateBaudRate(serialBaud *100);

#ifdef WLED_ENABLE_SYSLOG
syslogEnabled = request->hasArg(F("SL_en"));
strlcpy(syslogHost, request->arg(F("SL_host")).c_str(), sizeof(syslogHost));

t = request->arg(F("SL_port")).toInt();
if (t > 0) syslogPort = t;

t = request->arg(F("SL_proto")).toInt();
if (t >= SYSLOG_PROTO_BSD && t <= SYSLOG_PROTO_RAW) syslogProtocol = t;

t = request->arg(F("SL_fac")).toInt();
if (t >= SYSLOG_KERN && t <= SYSLOG_LOCAL7) syslogFacility = t;

t = request->arg(F("SL_sev")).toInt();
if (t >= SYSLOG_EMERG && t <= SYSLOG_DEBUG) syslogSeverity = t;

Syslog.begin(syslogHost, syslogPort,
syslogFacility, syslogSeverity, syslogProtocol);

Syslog.setAppName("WLED");
#endif
}

//TIME
Expand Down
Loading