Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
153 changes: 153 additions & 0 deletions pio-scripts/inject_syslog_ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# pio-scripts/inject_syslog_ui.py

"""
PlatformIO build script to conditionally inject Syslog UI elements into the settings HTML file.

This script:
1. Injects Syslog UI elements when WLED_ENABLE_SYSLOG is defined in build flags
2. Restores the original HTML file after build completion
3. Tracks state between builds to force UI rebuilds when necessary
"""

import os, re, shutil
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="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>
</div>"""
Comment on lines +34 to +39
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the html is so much smaller, i wonder if we really need the complication of this selective injection or if we should follow the pattern used for optional settings that is used for DMX Input support

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean move the html part to the settings_sync.htm with "hide function" and remove the injection py script?

like i had it in the begining and had to change it because it adds size?


def inject_syslog_ui(source, target, env, retry_count=0):
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 = bool(re.search(r'-D\s*WLED_ENABLE_SYSLOG\b', 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:
with open(html_path, 'r', encoding='utf8') as f:
original = f.read()
modified = original

# replace the single comment with HTML
if '<!-- SYSLOG-INJECT -->' in modified:
modified = modified.replace('<!-- SYSLOG-INJECT -->', SYSLOG_HTML)
else:
# insert before last <hr>
idx = modified.rfind('<hr>')
if idx == -1:
print("\033[41mCould not find <hr> to insert Syslog UI!\033[0m")
# Clean up backup since injection failed
if os.path.exists(bak):
os.remove(bak)
return
modified = (
modified[:idx]
+ SYSLOG_HTML + '\n'
+ modified[idx:]
)

with open(html_path, 'w', encoding='utf8') as f:
f.write(modified)
print("\033[42mSyslog UI injected successfully!\033[0m")

except (IOError, OSError) as e:
print(f"\033[41mFile operation error during injection: {e}\033[0m")
# injection failed → remove backup so we'll retry next time
if os.path.exists(bak):
os.remove(bak)
except Exception as e:
print(f"\033[41mUnexpected error during injection: {e}\033[0m")
# injection failed → remove backup so we’ll retry next time
if os.path.exists(bak):
os.remove(bak)
else:
print("Backup exists; assume already injected.")
# verify that SYSLOG markers really are in the file
with open(html_path, 'r', encoding='utf8') as f:
content = f.read()
if '<h3>Syslog</h3>' not in content:
print("Backup exists but SYSLOG markers missing—forcing re-injection.")
os.remove(bak)
# only retry up to 3 times
if retry_count < 3:
# Add a small delay before retrying
import time
time.sleep(0.5 * (retry_count + 1)) # Increasing delay with each retry
inject_syslog_ui(source, target, env, retry_count + 1)
else:
print("\033[41mToo many retry attempts. Manual intervention required.\033[0m")
else:
print("Backup exists and markers found; 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...")
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
14 changes: 14 additions & 0 deletions wled00/cfg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,13 @@ 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"]);
#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 +1058,13 @@ 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;
#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-INJECT -->
<hr>
<button type="button" onclick="B()">Back</button><button type="submit">Save</button>
</form>
Expand Down
9 changes: 8 additions & 1 deletion wled00/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,11 @@ 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;
configNeedsWrite = true;
DEBUG_PRINTF_P(PSTR("Syslog: %s\n"), syslogEnabled ? PSTR("ENABLED") : PSTR("DISABLED") );
#endif

bool onBefore = bri;
Expand Down Expand Up @@ -781,6 +785,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
13 changes: 13 additions & 0 deletions wled00/set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,19 @@ 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;

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

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

//TIME
Expand Down
Loading