feat(secubox-core): Add triple-pulse LED heartbeat with staggered cascade

Implements organic "bump-bump-bump (pause)" pattern across 3 RGB LEDs:
- LED1 (health) leads the pulse sequence
- LED2 (threat) follows with offset timing (décalé)
- LED3 (capacity) trails as final beat in cascade
- Smooth intensity transitions between beats
- Subtle breathing effect during rest period
- Event pulse override preserved for alerts

Pattern timing: beat1 → gap → beat2 → gap → beat3 → rest (0.15s ticks)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-02-06 17:22:49 +01:00
parent 301dccec33
commit a47ae9656c

View File

@ -1094,30 +1094,139 @@ led_event_pulse() {
return 0 return 0
} }
# Triple-pulse heartbeat with staggered cascade across LEDs
# Creates organic "bump-bump-bump (pause)" pattern
# Each LED pulses sequentially with offset timing (décalé)
LED_PULSE_PHASE=0 # Tracks which LED is in primary pulse
led_triple_pulse() {
local led_num="$1"
local r="$2" g="$3" b="$4"
local intensity="$5" # 0-100 for pulse strength
# Calculate dimmed color based on intensity
local ri=$((r * intensity / 100))
local gi=$((g * intensity / 100))
local bi=$((b * intensity / 100))
led_set_rgb "$led_num" "$ri" "$gi" "$bi"
}
# Fast LED heartbeat background loop # Fast LED heartbeat background loop
# Runs independently at 1.5s intervals for reactive visual feedback # Runs independently with triple-pulse cascade effect
# led1: Global health status # led1: Global health status (primary pulse)
# led2: Security threat level # led2: Security threat level (offset pulse)
# led3: Global capacity meter (CPU + Network) with event pulse overlay # led3: Global capacity meter (trailing pulse)
# Reads from pre-computed cache files (no subprocess calls) # Pattern: bump1-bump2-bump3 (pause) with staggered timing
led_heartbeat_loop() { led_heartbeat_loop() {
local status_file="/tmp/secubox/led-status" local status_file="/tmp/secubox/led-status"
echo "healthy" > "$status_file" echo "healthy" > "$status_file"
# Base colors for each LED (set by status)
local r1=0 g1=255 b1=0 # Health: green
local r2=0 g2=255 b2=0 # Threat: green
local r3=0 g3=255 b3=0 # Capacity: green
# Pulse timing (in 0.1s units for fine control)
local beat_gap=1 # Gap between beats in triple
local cycle_pause=8 # Pause after triple (longer rest)
local tick=0 # Current tick in cycle
while true; do while true; do
local status=$(cat "$status_file" 2>/dev/null || echo "healthy") local status=$(cat "$status_file" 2>/dev/null || echo "healthy")
# Update RGB LEDs (reads from cache - instant) # Update base colors from cache (every cycle start)
led_heartbeat "$status" if [ "$tick" -eq 0 ]; then
# LED1: Health color
local health=$(get_health_score)
if [ "$health" -ge 80 ]; then
r1=0; g1=255; b1=50
elif [ "$health" -ge 50 ]; then
r1=255; g1=200; b1=0
else
r1=255; g1=30; b1=0
fi
# Check for event pulse first (overrides capacity momentarily) # LED2: Threat color
if led_event_pulse; then local threat=$(get_threat_level)
sleep 0.3 # Brief flash if [ "$threat" -le 10 ]; then
r2=0; g2=255; b2=0
elif [ "$threat" -le 40 ]; then
r2=200; g2=255; b2=0
elif [ "$threat" -le 70 ]; then
r2=255; g2=150; b2=0
else
r2=255; g2=0; b2=0
fi
# LED3: Capacity color (from cache)
local cap=$(get_capacity)
if [ "$cap" -le 30 ]; then
r3=0; g3=255; b3=100
elif [ "$cap" -le 60 ]; then
r3=100; g3=255; b3=0
elif [ "$cap" -le 80 ]; then
r3=255; g3=200; b3=0
else
r3=255; g3=80; b3=0
fi
fi fi
# Global capacity on led3 # Check for event pulse (overrides normal pattern)
led_global_capacity if led_event_pulse; then
sleep 1.2 sleep 0.3
tick=0
continue
fi
# Triple-pulse cascade pattern with décalé (staggered) timing
# Cycle: 0=beat1, 1=gap, 2=beat2, 3=gap, 4=beat3, 5-12=rest
local total_cycle=$((3 + 2 * beat_gap + cycle_pause))
local phase=$((tick % total_cycle))
# Calculate intensities for each LED based on phase
# LED1 leads, LED2 offset by 1 beat, LED3 offset by 2 beats
local int1=30 int2=30 int3=30 # Base dim level
case "$phase" in
0) # First beat - LED1 bright, others dim
int1=100
;;
1) # Gap - LED1 fading, LED2 rising
int1=60
int2=80
;;
2) # Second beat - LED2 bright, LED1 dim, LED3 rising
int1=40
int2=100
int3=60
;;
3) # Gap - LED2 fading, LED3 rising
int2=60
int3=80
;;
4) # Third beat - LED3 bright, others dim
int3=100
;;
5) # Post-triple fade
int1=20; int2=20; int3=50
;;
*) # Rest period - all dim with subtle breathing
local breath=$((30 + (phase % 3) * 5))
int1=$breath; int2=$breath; int3=$breath
;;
esac
# Apply intensities
led_triple_pulse 1 "$r1" "$g1" "$b1" "$int1"
led_triple_pulse 2 "$r2" "$g2" "$b2" "$int2"
led_triple_pulse 3 "$r3" "$g3" "$b3" "$int3"
tick=$((tick + 1))
[ "$tick" -ge "$total_cycle" ] && tick=0
# Fine timing: 0.15s per tick for smooth animation
sleep 0.15
done done
} }