#!/bin/sh CONFIG="led-scheduler" SECTION="main" LED_ROOT="/sys/class/leds" CRON_FILE="/etc/crontabs/root" CRON_BEGIN="# BEGIN luci-app-led-scheduler" CRON_END="# END luci-app-led-scheduler" log() { logger -t led-scheduler "$*"; } uci_get() { uci -q get "${CONFIG}.${SECTION}.$1" 2>/dev/null; } valid_led_name() { case "$1" in ""|*/*|*".."*|*[!A-Za-z0-9:_.-]*) return 1 ;; esac return 0 } available_led() { valid_led_name "$1" || return 1 [ -d "${LED_ROOT}/$1" ] } configured_leds() { local section sysfs uci -q show system 2>/dev/null | sed -n 's/^system\.\([^=]*\)=led$/\1/p' | while IFS= read -r section; do sysfs="$(uci -q get "system.${section}.sysfs" 2>/dev/null)" [ -n "$sysfs" ] || continue available_led "$sysfs" || continue printf '%s\n' "$sysfs" done | awk '!seen[$0]++' } selected_leds() { local all selected led all="$(uci_get all_system_leds)" [ -n "$all" ] || all="1" if [ "$all" = "1" ]; then configured_leds return 0 fi selected="$(uci_get led)" for led in $selected; do available_led "$led" || continue printf '%s\n' "$led" done | awk '!seen[$0]++' } restore_native() { if [ -x /etc/init.d/led ]; then /etc/init.d/led restart >/dev/null 2>&1 return $? fi echo "OpenWrt LED service /etc/init.d/led not found" >&2 return 1 } turn_off() { local led path restore_native >/dev/null 2>&1 || true selected_leds | while IFS= read -r led; do available_led "$led" || continue path="${LED_ROOT}/${led}" [ -w "${path}/trigger" ] && printf '%s\n' none > "${path}/trigger" 2>/dev/null [ -w "${path}/brightness" ] && printf '%s\n' 0 > "${path}/brightness" 2>/dev/null done log "selected LEDs turned off" } turn_on() { if restore_native; then log "native OpenWrt LED configuration restored" return 0 fi log "failed to restore native LED configuration" return 1 } validate_time() { local t="$1" h m case "$t" in [0-9][0-9]:[0-9][0-9]) ;; *) return 1 ;; esac h="${t%:*}"; m="${t#*:}" h="${h#0}"; m="${m#0}" [ -n "$h" ] || h=0; [ -n "$m" ] || m=0 [ "$h" -ge 0 ] 2>/dev/null || return 1 [ "$h" -le 23 ] 2>/dev/null || return 1 [ "$m" -ge 0 ] 2>/dev/null || return 1 [ "$m" -le 59 ] 2>/dev/null || return 1 return 0 } time_parts() { local t="$1" h m h="${t%:*}"; m="${t#*:}" h="${h#0}"; m="${m#0}" [ -n "$h" ] || h=0; [ -n "$m" ] || m=0 printf '%s %s\n' "$h" "$m" } strip_cron_block() { local tmp="/tmp/led-scheduler-cron.$$" mkdir -p "$(dirname "$CRON_FILE")" touch "$CRON_FILE" awk -v begin="$CRON_BEGIN" -v end="$CRON_END" ' $0 == begin { skip=1; next } $0 == end { skip=0; next } !skip { print } ' "$CRON_FILE" > "$tmp" cat "$tmp" > "$CRON_FILE" rm -f "$tmp" } reload_cron() { [ -x /etc/init.d/cron ] && /etc/init.d/cron restart >/dev/null 2>&1; } clear_cron() { strip_cron_block reload_cron log "cron entries removed" } sync_cron() { local enabled off_time on_time off_h off_m on_h on_m enabled="$(uci_get enabled)"; [ -n "$enabled" ] || enabled="0" off_time="$(uci_get off_time)"; on_time="$(uci_get on_time)" [ -n "$off_time" ] || off_time="23:00" [ -n "$on_time" ] || on_time="07:00" strip_cron_block if [ "$enabled" != "1" ]; then reload_cron; log "scheduler disabled; cron entries removed"; return 0; fi validate_time "$off_time" || { echo "Invalid off_time: $off_time" >&2; return 1; } validate_time "$on_time" || { echo "Invalid on_time: $on_time" >&2; return 1; } set -- $(time_parts "$off_time"); off_h="$1"; off_m="$2" set -- $(time_parts "$on_time"); on_h="$1"; on_m="$2" { printf '%s\n' "$CRON_BEGIN" printf '%s %s * * * /usr/bin/led-control off\n' "$off_m" "$off_h" printf '%s %s * * * /usr/bin/led-control on\n' "$on_m" "$on_h" printf '%s\n' "$CRON_END" } >> "$CRON_FILE" reload_cron log "cron synchronized: off=${off_time}, on=${on_time}" } minutes_from_time() { local t="$1" h m set -- $(time_parts "$t"); h="$1"; m="$2" echo $((h * 60 + m)) } apply_current_state() { local enabled off_time on_time now_h now_m now off on enabled="$(uci_get enabled)" if [ "$enabled" != "1" ]; then turn_on; return $?; fi off_time="$(uci_get off_time)"; on_time="$(uci_get on_time)" [ -n "$off_time" ] || off_time="23:00" [ -n "$on_time" ] || on_time="07:00" validate_time "$off_time" || return 1 validate_time "$on_time" || return 1 off="$(minutes_from_time "$off_time")"; on="$(minutes_from_time "$on_time")" if [ "$off" -eq "$on" ]; then turn_on; return $?; fi now_h="$(date +%H)"; now_m="$(date +%M)" now_h="${now_h#0}"; now_m="${now_m#0}" [ -n "$now_h" ] || now_h=0; [ -n "$now_m" ] || now_m=0 now=$((now_h * 60 + now_m)) if [ "$off" -lt "$on" ]; then if [ "$now" -ge "$off" ] && [ "$now" -lt "$on" ]; then turn_off; else turn_on; fi else if [ "$now" -ge "$off" ] || [ "$now" -lt "$on" ]; then turn_off; else turn_on; fi fi } list_configured() { local section name sysfs trigger uci -q show system 2>/dev/null | sed -n 's/^system\.\([^=]*\)=led$/\1/p' | while IFS= read -r section; do name="$(uci -q get "system.${section}.name" 2>/dev/null)" sysfs="$(uci -q get "system.${section}.sysfs" 2>/dev/null)" trigger="$(uci -q get "system.${section}.trigger" 2>/dev/null)" [ -n "$sysfs" ] || continue printf '%s %s %s\n' "${name:-$sysfs}" "$sysfs" "${trigger:-unknown}" done } status() { echo "enabled=$(uci_get enabled)" echo "off_time=$(uci_get off_time)" echo "on_time=$(uci_get on_time)" echo "all_system_leds=$(uci_get all_system_leds)" echo "selected sysfs LEDs:" selected_leds | sed 's/^/ - /' } usage() { cat <<'EOF' Usage: led-control COMMAND Commands: list List LEDs defined by OpenWrt System -> LED Configuration selected List LEDs currently selected by LED Scheduler off Turn selected LEDs off on Restore OpenWrt native LED configuration apply Apply the correct state for the current time sync Synchronize cron entries from UCI configuration clear-cron Remove this application's cron entries status Show scheduler configuration EOF } case "$1" in list) list_configured ;; selected) selected_leds ;; off) turn_off ;; on) turn_on ;; apply) apply_current_state ;; sync) sync_cron ;; clear-cron) clear_cron ;; status) status ;; *) usage; exit 1 ;; esac