Last active
March 10, 2026 12:18
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 "" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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