Skip to content

Instantly share code, notes, and snippets.

@rehannali
Last active March 10, 2026 12:18
Show Gist options
  • Select an option

  • Save rehannali/3d6f2c00be616688f697b8d35af83361 to your computer and use it in GitHub Desktop.

Select an option

Save rehannali/3d6f2c00be616688f697b8d35af83361 to your computer and use it in GitHub Desktop.
pfSense temperature monitor and notify if something happened. This also include daily, weekly and monthly report.
#!/bin/sh
#
# Installation script for pfSense Temperature Monitoring System
#
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ pfSense Temperature Monitoring System - Installation ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
# Check if running as root
if [ "$(id -u)" != "0" ]; then
echo "❌ This script must be run as root"
exit 1
fi
echo "������ Installing temperature monitoring scripts..."
echo ""
# Make scripts executable
chmod +x /usr/local/bin/temp_monitor.sh 2>/dev/null && echo "✓ temp_monitor.sh - executable"
chmod +x /usr/local/bin/temp_report.sh 2>/dev/null && echo "✓ temp_report.sh - executable"
chmod +x /usr/local/bin/temp_dashboard.sh 2>/dev/null && echo "✓ temp_dashboard.sh - executable"
# Create log directory if needed
LOG_DIR="/var/log"
if [ ! -d "$LOG_DIR" ]; then
mkdir -p "$LOG_DIR"
echo "✓ Created log directory"
fi
# Test temperature reading
echo ""
echo "������ Testing temperature sensors..."
TEMP=$(sysctl -n dev.cpu.0.temperature 2>/dev/null)
if [ -n "$TEMP" ]; then
echo "✓ Temperature sensors detected: $TEMP"
else
echo "❌ Warning: Unable to read temperature sensors"
fi
# Check notification configuration
echo ""
echo "������ Checking pfSense notification configuration..."
/usr/local/bin/temp_monitor.sh --check-config
echo ""
echo "������ Testing notification system..."
echo " (This will send a test message to all enabled channels)"
read -p " Send test notification? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
/usr/local/bin/temp_monitor.sh --test
fi
echo ""
echo "⏰ Setting up cron job..."
# Check if cron job already exists
if grep -q "temp_monitor.sh" /etc/crontab 2>/dev/null; then
echo "✓ Cron job already configured"
else
echo "*/5 * * * * root /usr/local/bin/temp_monitor.sh" >> /etc/crontab
echo "✓ Added cron job (runs every 5 minutes)"
fi
echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ Installation Complete! ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
echo "������ Quick Start:"
echo " Check status: /usr/local/bin/temp_monitor.sh --status"
echo " View dashboard: /usr/local/bin/temp_dashboard.sh"
echo " Generate report: /usr/local/bin/temp_report.sh daily"
echo " Check config: /usr/local/bin/temp_monitor.sh --check-config"
echo " Send test alert: /usr/local/bin/temp_monitor.sh --test"
echo ""
echo "������ Log file: /var/log/temperature_monitor.log"
echo "������ View logs: /usr/local/bin/temp_monitor.sh --log"
echo ""
echo "⚙️ Next Steps:"
echo " 1. Configure notifications in pfSense GUI:"
echo " System → Advanced → Notifications"
echo " 2. Adjust thresholds in /usr/local/bin/temp_monitor.sh"
echo " 3. Enable/disable channels as needed"
echo ""
#!/bin/sh
#
# Real-time Temperature Dashboard for pfSense
# Press Ctrl+C to exit
#
REFRESH_INTERVAL=2
while true; do
clear
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║ pfSense Temperature Monitor - Live Dashboard ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')"
echo "Hostname: $(hostname)"
echo "Uptime: $(uptime | awk '{print $3,$4}' | sed 's/,//')"
echo ""
echo "══════════════════════════════════════════════════════════════"
echo "CPU Core Temperatures"
echo "══════════════════════════════════════════════════════════════"
# Display each core with status indicator
sysctl dev.cpu | grep temperature | while read line; do
core=$(echo "$line" | grep -oE "dev.cpu.[0-9]" | grep -oE "[0-9]")
temp=$(echo "$line" | awk -F': ' '{print $2}')
temp_num=$(echo "$temp" | cut -d'.' -f1 | sed 's/C//')
# Status indicator based on temperature
if [ "$temp_num" -lt 50 ]; then
status="✓ EXCELLENT"
bar="████████████████████"
elif [ "$temp_num" -lt 60 ]; then
status="○ GOOD "
bar="██████████████░░░░░░"
elif [ "$temp_num" -lt 70 ]; then
status="△ WARM "
bar="████████████░░░░░░░░"
elif [ "$temp_num" -lt 80 ]; then
status="⚠ HOT "
bar="████████░░░░░░░░░░░░"
else
status="✗ CRITICAL"
bar="████░░░░░░░░░░░░░░░░"
fi
printf " Core %d: %7s [%s] %s\n" "$core" "$temp" "$bar" "$status"
done
echo ""
echo "══════════════════════════════════════════════════════════════"
echo "Statistics"
echo "══════════════════════════════════════════════════════════════"
MAX=$(sysctl dev.cpu | grep temperature | awk -F': ' '{print $2}' | sed 's/C//' | sort -rn | head -1)
MIN=$(sysctl dev.cpu | grep temperature | awk -F': ' '{print $2}' | sed 's/C//' | sort -n | head -1)
AVG=$(sysctl dev.cpu | grep temperature | awk -F': ' '{print $2}' | sed 's/C//' | awk '{sum+=$1; count++} END {printf "%.1f", sum/count}')
FREQ=$(sysctl -n dev.cpu.0.freq 2>/dev/null || echo "N/A")
printf " Maximum: %.1f°C\n" "$MAX"
printf " Minimum: %.1f°C\n" "$MIN"
printf " Average: %.1f°C\n" "$AVG"
printf " CPU Freq: %s MHz\n" "$FREQ"
echo ""
echo "══════════════════════════════════════════════════════════════"
echo "Thermal Zones"
echo "══════════════════════════════════════════════════════════════"
TZ_TEMP=$(sysctl -n hw.acpi.thermal.tz0.temperature 2>/dev/null || echo "N/A")
TZ_CRIT=$(sysctl -n hw.acpi.thermal.tz0._CRT 2>/dev/null || echo "N/A")
TZ_HOT=$(sysctl -n hw.acpi.thermal.tz0._HOT 2>/dev/null || echo "N/A")
printf " ACPI TZ0: %s\n" "$TZ_TEMP"
printf " Critical: %s\n" "$TZ_CRIT"
if [ "$TZ_HOT" != "N/A" ] && [ "$TZ_HOT" != "-1" ]; then
printf " Hot: %s\n" "$TZ_HOT"
fi
echo ""
echo "══════════════════════════════════════════════════════════════"
echo "Press Ctrl+C to exit | Refreshing every ${REFRESH_INTERVAL}s"
echo "══════════════════════════════════════════════════════════════"
sleep $REFRESH_INTERVAL
done
#!/bin/sh
#
# pfSense Temperature Monitor with Multi-Channel Notifications
# Optimized for Intel N100 (fanless operation)
# Supports: Email, Telegram, Slack, Pushover via pfSense native system
#
##############################################################################
# CONFIGURATION - Adjust these values
##############################################################################
# Temperature Thresholds (in Celsius)
TEMP_INFO=60 # Info level - log only
TEMP_WARNING=70 # Warning - send notification
TEMP_CRITICAL=80 # Critical - urgent notification
TEMP_EMERGENCY=90 # Emergency - repeated notifications
# Notification Channel Toggles (true/false)
ENABLE_EMAIL=true
ENABLE_TELEGRAM=true
ENABLE_SLACK=true
ENABLE_PUSHOVER=true
# Use notify_all_remote() to send to ALL configured channels at once
# If false, uses individual toggles above
USE_UNIFIED=false
# Logging Configuration
LOG_FILE="/var/log/temperature_monitor.log"
LOG_READINGS=true # Log every reading
LOG_RETENTION_DAYS=30 # Keep logs for 30 days
# Notification Cooldown (seconds) - prevents spam
COOLDOWN_WARNING=1800 # 30 minutes between warning alerts
COOLDOWN_CRITICAL=600 # 10 minutes between critical alerts
COOLDOWN_EMERGENCY=300 # 5 minutes between emergency alerts
# State file for cooldown tracking
STATE_FILE="/tmp/temp_monitor_state"
##############################################################################
# HELPER FUNCTIONS
##############################################################################
# Get current timestamp
get_timestamp() {
date "+%Y-%m-%d %H:%M:%S"
}
# Get all CPU core temperatures
get_cpu_temps() {
sysctl dev.cpu | grep temperature | awk -F': ' '{print $2}' | sed 's/C//'
}
# Get maximum CPU temperature across all cores
get_max_temp() {
get_cpu_temps | sort -rn | head -1 | cut -d'.' -f1
}
# Get average CPU temperature
get_avg_temp() {
get_cpu_temps | awk '{sum+=$1; count++} END {printf "%.1f", sum/count}'
}
# Get current CPU frequency in MHz
get_cpu_freq() {
sysctl -n dev.cpu.0.freq 2>/dev/null || echo "N/A"
}
# Log temperature reading to file and syslog
log_reading() {
local max_temp=$1
local avg_temp=$2
local freq=$3
local timestamp=$(get_timestamp)
if [ "$LOG_READINGS" = "true" ]; then
echo "$timestamp | MAX: ${max_temp}°C | AVG: ${avg_temp}°C | FREQ: ${freq}MHz" >> "$LOG_FILE"
fi
logger -t temp_monitor "CPU Temp - Max: ${max_temp}°C, Avg: ${avg_temp}°C, Freq: ${freq}MHz"
}
# Check if cooldown period has passed since last notification
check_cooldown() {
local level=$1
local cooldown_var="COOLDOWN_${level}"
local cooldown_time=$(eval echo \$$cooldown_var)
if [ ! -f "$STATE_FILE" ]; then
return 0 # No state file, allow notification
fi
local last_alert=$(grep "^${level}=" "$STATE_FILE" 2>/dev/null | cut -d'=' -f2)
if [ -z "$last_alert" ]; then
return 0 # No previous alert, allow
fi
local current_time=$(date +%s)
local time_diff=$((current_time - last_alert))
if [ $time_diff -ge $cooldown_time ]; then
return 0 # Cooldown passed, allow
else
return 1 # Still in cooldown, block
fi
}
# Update cooldown state after sending notification
update_cooldown() {
local level=$1
local current_time=$(date +%s)
# Remove old entry if exists
if [ -f "$STATE_FILE" ]; then
grep -v "^${level}=" "$STATE_FILE" > "${STATE_FILE}.tmp" 2>/dev/null
mv "${STATE_FILE}.tmp" "$STATE_FILE" 2>/dev/null
fi
# Add new timestamp
echo "${level}=${current_time}" >> "$STATE_FILE"
}
# Send notifications via pfSense native notification system
send_notification() {
local title=$1
local message=$2
# Escape single quotes for PHP
local escaped_title=$(echo "$title" | sed "s/'/\\\'/g")
local escaped_message=$(echo "$message" | sed "s/'/\\\'/g")
if [ "$USE_UNIFIED" = "true" ]; then
# Send to ALL configured channels at once
/usr/local/bin/php << PHPEOF
<?php
require_once('/etc/inc/globals.inc');
require_once('/etc/inc/config.inc');
require_once('/etc/inc/notices.inc');
\$title = '${escaped_title}';
\$message = '${escaped_message}';
// Send to all configured notification channels
notify_all_remote(\$message);
// Also create notice in pfSense GUI
file_notice('temp_monitor', \$message, \$title, '');
log_error("Temperature Monitor: Sent to all configured channels");
?>
PHPEOF
else
# Send to individual channels based on toggles
/usr/local/bin/php << PHPEOF
<?php
require_once('/etc/inc/globals.inc');
require_once('/etc/inc/config.inc');
require_once('/etc/inc/notices.inc');
\$title = '${escaped_title}';
\$message = '${escaped_message}';
\$channels_sent = array();
// Email (SMTP)
if ('${ENABLE_EMAIL}' === 'true') {
notify_via_smtp(\$message, true);
\$channels_sent[] = 'Email';
}
// Telegram
if ('${ENABLE_TELEGRAM}' === 'true') {
notify_via_telegram(\$message, true);
\$channels_sent[] = 'Telegram';
}
// Slack
if ('${ENABLE_SLACK}' === 'true') {
notify_via_slack(\$message, true);
\$channels_sent[] = 'Slack';
}
// Pushover
if ('${ENABLE_PUSHOVER}' === 'true') {
notify_via_pushover(\$message, true);
\$channels_sent[] = 'Pushover';
}
// Create notice in pfSense GUI
file_notice('temp_monitor', \$message, \$title, '');
// Log which channels were used
if (count(\$channels_sent) > 0) {
log_error("Temperature Monitor: Notification sent via " . implode(', ', \$channels_sent));
} else {
log_error("Temperature Monitor: No notification channels enabled");
}
?>
PHPEOF
fi
if [ $? -eq 0 ]; then
logger -t temp_monitor "Notification sent: $title"
return 0
else
logger -t temp_monitor "ERROR: Failed to send notification"
return 1
fi
}
# Main notification function with cooldown logic
notify() {
local level=$1
local max_temp=$2
local avg_temp=$3
local details=$4
# Check if any notification channel is enabled
if [ "$ENABLE_EMAIL" != "true" ] && \
[ "$ENABLE_TELEGRAM" != "true" ] && \
[ "$ENABLE_SLACK" != "true" ] && \
[ "$ENABLE_PUSHOVER" != "true" ] && \
[ "$USE_UNIFIED" != "true" ]; then
logger -t temp_monitor "All notification channels disabled"
return
fi
# Check cooldown
if ! check_cooldown "$level"; then
logger -t temp_monitor "Notification blocked by cooldown: $level"
return
fi
local hostname=$(hostname)
local timestamp=$(get_timestamp)
# Build notification message
local title="[$level] Temperature Alert - ${max_temp}°C"
local message="������️ Temperature Alert: ${level}
Firewall: ${hostname}
Timestamp: ${timestamp}
������ Current Status:
Maximum Temp: ${max_temp}°C
Average Temp: ${avg_temp}°C
${details}
⚙️ Alert Thresholds:
Warning: ${TEMP_WARNING}°C
Critical: ${TEMP_CRITICAL}°C
Emergency: ${TEMP_EMERGENCY}°C
������ Notification Channels: Email(${ENABLE_EMAIL}) Telegram(${ENABLE_TELEGRAM}) Slack(${ENABLE_SLACK}) Pushover(${ENABLE_PUSHOVER})"
# Send notification
send_notification "$title" "$message"
# Update cooldown
update_cooldown "$level"
}
# Cleanup old log files
cleanup_logs() {
if [ -f "$LOG_FILE" ]; then
find "$(dirname $LOG_FILE)" -name "$(basename $LOG_FILE)*" -mtime +$LOG_RETENTION_DAYS -delete 2>/dev/null
fi
}
# Check pfSense notification configuration status
check_notification_config() {
echo "Checking pfSense Notification Configuration..."
echo "=============================================="
echo ""
/usr/local/bin/php << 'PHPEOF'
<?php
require_once('/etc/inc/config.inc');
global $config;
// Check Email/SMTP
if (isset($config['notifications']['smtp']['ipaddress']) &&
$config['notifications']['smtp']['ipaddress'] != '') {
echo "✓ Email (SMTP): Configured (" . $config['notifications']['smtp']['ipaddress'] . ")\n";
} else {
echo "✗ Email (SMTP): NOT configured\n";
}
// Check Telegram
if (isset($config['notifications']['telegram']['api']) &&
$config['notifications']['telegram']['api'] != '') {
echo "✓ Telegram: Configured\n";
} else {
echo "✗ Telegram: NOT configured\n";
}
// Check Slack
if (isset($config['notifications']['slack']['api']) &&
$config['notifications']['slack']['api'] != '') {
$channel = isset($config['notifications']['slack']['channel']) ?
$config['notifications']['slack']['channel'] : 'default';
echo "✓ Slack: Configured (channel: $channel)\n";
} else {
echo "✗ Slack: NOT configured\n";
}
// Check Pushover
if (isset($config['notifications']['pushover']['apikey']) &&
$config['notifications']['pushover']['apikey'] != '') {
echo "✓ Pushover: Configured\n";
} else {
echo "✗ Pushover: NOT configured\n";
}
?>
PHPEOF
echo ""
echo "Script Configuration:"
echo "---------------------"
echo "Email enabled: $ENABLE_EMAIL"
echo "Telegram enabled: $ENABLE_TELEGRAM"
echo "Slack enabled: $ENABLE_SLACK"
echo "Pushover enabled: $ENABLE_PUSHOVER"
echo "Use unified: $USE_UNIFIED"
}
##############################################################################
# MAIN MONITORING LOGIC
##############################################################################
main() {
# Get current temperatures
MAX_TEMP=$(get_max_temp)
AVG_TEMP=$(get_avg_temp)
CPU_FREQ=$(get_cpu_freq)
# Validate temperature reading
if [ -z "$MAX_TEMP" ] || [ "$MAX_TEMP" = "" ]; then
logger -t temp_monitor "ERROR: Unable to read CPU temperature"
exit 1
fi
# Log reading
log_reading "$MAX_TEMP" "$AVG_TEMP" "$CPU_FREQ"
# Get all core temps for detailed reporting
CORE_TEMPS=$(sysctl dev.cpu | grep temperature | awk '{printf " CPU%d: %s\n", NR-1, $2}')
DETAILS="������ Core Temperatures:
${CORE_TEMPS}
������ CPU Info:
Model: Intel N100
Frequency: ${CPU_FREQ} MHz
TDP: 6W (fanless optimized)"
# Check thresholds and send notifications
if [ "$MAX_TEMP" -ge "$TEMP_EMERGENCY" ]; then
notify "EMERGENCY" "$MAX_TEMP" "$AVG_TEMP" "$DETAILS
⚠️⚠️⚠️ IMMEDIATE ACTION REQUIRED! ⚠️⚠️⚠️
CPU temperature in emergency range!
System may shutdown to prevent damage."
elif [ "$MAX_TEMP" -ge "$TEMP_CRITICAL" ]; then
notify "CRITICAL" "$MAX_TEMP" "$AVG_TEMP" "$DETAILS
⚠️ CRITICAL TEMPERATURE WARNING!
Check cooling system immediately.
Verify fans, airflow, and ambient temperature."
elif [ "$MAX_TEMP" -ge "$TEMP_WARNING" ]; then
notify "WARNING" "$MAX_TEMP" "$AVG_TEMP" "$DETAILS
⚠️ Temperature elevated above normal.
Monitor system closely.
Consider improving cooling if persistent."
elif [ "$MAX_TEMP" -ge "$TEMP_INFO" ]; then
logger -t temp_monitor "INFO: CPU temp ${MAX_TEMP}°C (above ${TEMP_INFO}°C info threshold)"
fi
# Cleanup old logs periodically (approximately once per 100 runs)
if [ $(($(date +%s) % 100)) -eq 0 ]; then
cleanup_logs
fi
}
##############################################################################
# COMMAND LINE INTERFACE
##############################################################################
case "${1}" in
--check-config)
check_notification_config
;;
--test)
echo "Sending test notification to all enabled channels..."
echo ""
MAX_TEMP=$(get_max_temp)
AVG_TEMP=$(get_avg_temp)
CPU_FREQ=$(get_cpu_freq)
send_notification "������ Test Alert" "This is a TEST notification from pfSense Temperature Monitor.
Current Status:
Max Temp: ${MAX_TEMP}°C
Avg Temp: ${AVG_TEMP}°C
CPU Freq: ${CPU_FREQ} MHz
All enabled notification channels should receive this message.
Timestamp: $(get_timestamp)
Hostname: $(hostname)"
echo ""
echo "Test notification sent!"
echo "Check: Email, Telegram, Slack, Pushover"
echo "Also check: System → Notifications in pfSense GUI"
;;
--status)
echo "Current Temperature Status"
echo "=========================="
echo "Timestamp: $(get_timestamp)"
echo ""
sysctl dev.cpu | grep temperature
echo ""
echo "Maximum: $(get_max_temp)°C"
echo "Average: $(get_avg_temp)°C"
echo "Frequency: $(get_cpu_freq) MHz"
echo ""
echo "Thresholds:"
echo " Info: ${TEMP_INFO}°C"
echo " Warning: ${TEMP_WARNING}°C"
echo " Critical: ${TEMP_CRITICAL}°C"
echo " Emergency: ${TEMP_EMERGENCY}°C"
;;
--log)
if [ -f "$LOG_FILE" ]; then
tail -20 "$LOG_FILE"
else
echo "Log file not found: $LOG_FILE"
fi
;;
*)
main
;;
esac
#!/bin/sh
#
# Temperature Report Generator for pfSense
# Fully automated - no interactive prompts
# Exit codes: 0=success, 1=no data, 2=error
#
##############################################################################
# CONFIGURATION
##############################################################################
LOG_FILE="/var/log/temperature_monitor.log"
REPORT_TYPE="${1:-daily}"
AUTO_SEND="${2:-no}" # Set to "yes" to auto-send, otherwise display only
# Notification settings
ENABLE_EMAIL=true
ENABLE_TELEGRAM=true
ENABLE_SLACK=true
ENABLE_PUSHOVER=true
##############################################################################
# FUNCTIONS
##############################################################################
get_date_range() {
case $REPORT_TYPE in
daily)
START_DATE=$(date -v-1d "+%Y-%m-%d")
TITLE="Daily Temperature Report"
;;
weekly)
START_DATE=$(date -v-7d "+%Y-%m-%d")
TITLE="Weekly Temperature Report"
;;
monthly)
START_DATE=$(date -v-30d "+%Y-%m-%d")
TITLE="Monthly Temperature Report"
;;
*)
echo "ERROR: Invalid report type. Use: daily, weekly, or monthly" >&2
exit 2
;;
esac
}
generate_report() {
local report_text=""
report_text="${report_text}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
$TITLE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Hostname: $(hostname)
Period: From $START_DATE to $(date '+%Y-%m-%d')
Generated: $(date '+%Y-%m-%d %H:%M:%S')
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
"
if [ ! -f "$LOG_FILE" ]; then
echo "ERROR: Log file not found: $LOG_FILE" >&2
return 1
fi
# Extract data for period - handles date ranges properly
case $REPORT_TYPE in
daily)
# For daily, search for exact date
DATA=$(grep "$START_DATE" "$LOG_FILE" 2>/dev/null)
;;
weekly|monthly)
# For weekly/monthly, get all data from START_DATE onwards
DATA=$(awk -v start="$START_DATE" '$1 >= start' "$LOG_FILE" 2>/dev/null)
;;
esac
if [ -z "$DATA" ]; then
echo "ERROR: No data found for period starting $START_DATE" >&2
return 1
fi
# Calculate statistics
MAX=$(echo "$DATA" | grep -oE "MAX: [0-9]+°C" | grep -oE "[0-9]+" | sort -rn | head -1)
MIN=$(echo "$DATA" | grep -oE "MAX: [0-9]+°C" | grep -oE "[0-9]+" | sort -n | head -1)
AVG=$(echo "$DATA" | grep -oE "MAX: [0-9]+°C" | grep -oE "[0-9]+" | awk '{sum+=$1; count++} END {printf "%.1f", sum/count}')
COUNT=$(echo "$DATA" | wc -l | tr -d ' ')
# Validate data
if [ -z "$MAX" ] || [ -z "$MIN" ] || [ -z "$AVG" ]; then
echo "ERROR: Failed to parse temperature data" >&2
return 1
fi
report_text="${report_text}������ Temperature Statistics
─────────────────────────────────────────────
Maximum Temperature: ${MAX}°C
Minimum Temperature: ${MIN}°C
Average Temperature: ${AVG}°C
Total Readings: ${COUNT}
"
# Temperature distribution
DISTRIBUTION=$(echo "$DATA" | grep -oE "MAX: [0-9]+°C" | grep -oE "[0-9]+" | \
awk '{
if ($1 < 40) cool++
else if ($1 < 50) normal++
else if ($1 < 60) warm++
else if ($1 < 70) hot++
else critical++
total++
}
END {
if (total > 0) {
printf " < 40°C (Cool): %4d readings (%.1f%%)\n", cool, (cool/total)*100
printf " 40-49°C (Normal): %4d readings (%.1f%%)\n", normal, (normal/total)*100
printf " 50-59°C (Warm): %4d readings (%.1f%%)\n", warm, (warm/total)*100
printf " 60-69°C (Hot): %4d readings (%.1f%%)\n", hot, (hot/total)*100
printf " ≥ 70°C (Critical): %4d readings (%.1f%%)\n", critical, (critical/total)*100
}
}')
report_text="${report_text}������ Temperature Distribution
─────────────────────────────────────────────
${DISTRIBUTION}
"
# Peak temperatures - Different approach for each report type
case $REPORT_TYPE in
daily)
# For daily: Show peak by hour
HOURLY=$(echo "$DATA" | grep -oE "[0-9]{2}:[0-9]{2}:[0-9]{2}.*MAX: [0-9]+" | \
sed 's/^\([0-9][0-9]\):[0-9][0-9]:[0-9][0-9].*MAX: \([0-9][0-9]*\).*/\1 \2/' | \
awk '{
if ($2 > max[$1]) max[$1] = $2
}
END {
for (h in max) printf " Hour %02d:00 - Peak: %d°C\n", h, max[h]
}' | sort)
if [ -n "$HOURLY" ]; then
report_text="${report_text}������ Peak Temperatures by Hour
─────────────────────────────────────────────
${HOURLY}
"
fi
;;
weekly|monthly)
# For weekly/monthly: Show top temperature events
TOP_PEAKS=$(echo "$DATA" | grep -oE "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.*MAX: [0-9]+" | \
sed 's/^\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]\):[0-9][0-9].*MAX: \([0-9][0-9]*\).*/\2 \1/' | \
sort -rn | head -15 | \
awk '{printf " %s - %d°C\n", $2, $1}')
if [ -n "$TOP_PEAKS" ]; then
report_text="${report_text}������ Top 15 Highest Temperature Events
─────────────────────────────────────────────
${TOP_PEAKS}
"
fi
# Also show daily peaks
DAILY_PEAKS=$(echo "$DATA" | grep -oE "[0-9]{4}-[0-9]{2}-[0-9]{2}.*MAX: [0-9]+" | \
sed 's/^\([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\).*MAX: \([0-9][0-9]*\).*/\1 \2/' | \
awk '{
if ($2 > max[$1]) max[$1] = $2
}
END {
for (d in max) printf " %s - Peak: %d°C\n", d, max[d]
}' | sort)
if [ -n "$DAILY_PEAKS" ]; then
report_text="${report_text}������ Daily Peak Temperatures
─────────────────────────────────────────────
${DAILY_PEAKS}
"
fi
;;
esac
# Health assessment
if [ "$MAX" -lt 50 ]; then
HEALTH="✓ EXCELLENT - All temperatures in optimal range"
elif [ "$MAX" -lt 60 ]; then
HEALTH="✓ GOOD - Temperatures normal"
elif [ "$MAX" -lt 70 ]; then
HEALTH="⚠ FAIR - Some elevated temperatures detected"
elif [ "$MAX" -lt 80 ]; then
HEALTH="⚠ WARNING - High temperatures detected"
else
HEALTH="❌ CRITICAL - Dangerous temperatures detected!"
fi
report_text="${report_text}������ System Health Assessment
─────────────────────────────────────────────
Status: ${HEALTH}
Max Observed: ${MAX}°C
Avg Observed: ${AVG}°C
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "$report_text"
return 0
}
send_report_to_channels() {
local title=$1
local report=$2
# Validate input
if [ -z "$report" ]; then
logger -t temp_report "ERROR: Cannot send empty report"
return 1
fi
# Escape for PHP
local escaped_title=$(echo "$title" | sed "s/'/\\\'/g" | sed 's/"/\\"/g')
local escaped_report=$(echo "$report" | sed "s/'/\\\'/g" | sed 's/"/\\"/g')
# Send via PHP with timeout protection
timeout 30 /usr/local/bin/php << PHPEOF 2>/dev/null
<?php
require_once('/etc/inc/globals.inc');
require_once('/etc/inc/config.inc');
require_once('/etc/inc/notices.inc');
\$title = '${escaped_title}';
\$message = '${escaped_report}';
\$channels_sent = array();
\$errors = array();
// Email (SMTP)
if ('${ENABLE_EMAIL}' === 'true') {
try {
notify_via_smtp(\$message, true, \$title);
\$channels_sent[] = 'Email';
} catch (Exception \$e) {
\$errors[] = 'Email: ' . \$e->getMessage();
}
}
// Telegram
if ('${ENABLE_TELEGRAM}' === 'true') {
try {
notify_via_telegram(\$message, true);
\$channels_sent[] = 'Telegram';
} catch (Exception \$e) {
\$errors[] = 'Telegram: ' . \$e->getMessage();
}
}
// Slack
if ('${ENABLE_SLACK}' === 'true') {
try {
notify_via_slack(\$message, true);
\$channels_sent[] = 'Slack';
} catch (Exception \$e) {
\$errors[] = 'Slack: ' . \$e->getMessage();
}
}
// Pushover
if ('${ENABLE_PUSHOVER}' === 'true') {
try {
notify_via_pushover(\$message, true);
\$channels_sent[] = 'Pushover';
} catch (Exception \$e) {
\$errors[] = 'Pushover: ' . \$e->getMessage();
}
}
// Create notice in pfSense GUI
file_notice('temp_report', \$message, \$title, '');
// Log results
if (count(\$channels_sent) > 0) {
log_error("Temperature Report: Sent successfully via " . implode(', ', \$channels_sent));
echo "SUCCESS: Sent via " . implode(', ', \$channels_sent) . "\n";
exit(0);
} else {
log_error("Temperature Report: Failed to send - " . implode('; ', \$errors));
echo "ERROR: Failed to send report\n";
exit(1);
}
?>
PHPEOF
return $?
}
##############################################################################
# MAIN
##############################################################################
# Parse command line
case "$1" in
daily|weekly|monthly)
REPORT_TYPE="$1"
;;
--help|-h)
echo "Usage: $0 {daily|weekly|monthly} [--send]"
echo ""
echo "Options:"
echo " daily Generate daily report (last 24 hours)"
echo " weekly Generate weekly report (last 7 days)"
echo " monthly Generate monthly report (last 30 days)"
echo " --send Automatically send to all configured channels"
echo ""
echo "Examples:"
echo " $0 daily # Display report only"
echo " $0 daily --send # Generate and send report"
echo " $0 weekly --send # Generate and send weekly report"
exit 0
;;
*)
echo "ERROR: Invalid argument. Use --help for usage information." >&2
exit 2
;;
esac
# Check if auto-send is requested
if [ "$2" = "--send" ] || [ "$2" = "-s" ]; then
AUTO_SEND="yes"
fi
# Get date range
get_date_range
# Generate report
REPORT=$(generate_report 2>&1)
REPORT_STATUS=$?
# Check if report generation was successful
if [ $REPORT_STATUS -ne 0 ]; then
echo "ERROR: Failed to generate report" >&2
logger -t temp_report "ERROR: Failed to generate $REPORT_TYPE report"
exit 1
fi
# Validate report content
if [ -z "$REPORT" ]; then
echo "ERROR: Generated report is empty" >&2
logger -t temp_report "ERROR: Generated empty $REPORT_TYPE report"
exit 1
fi
# Display report
echo "$REPORT"
# Send to channels if requested
if [ "$AUTO_SEND" = "yes" ]; then
echo ""
echo "Sending report to notification channels..."
if send_report_to_channels "������ $TITLE" "$REPORT"; then
echo "✓ Report sent successfully!"
logger -t temp_report "SUCCESS: $REPORT_TYPE report sent to all channels"
exit 0
else
echo "✗ Failed to send report" >&2
logger -t temp_report "ERROR: Failed to send $REPORT_TYPE report"
exit 1
fi
else
# Interactive mode - not for automation
logger -t temp_report "INFO: $REPORT_TYPE report generated (display only)"
exit 0
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment