Homebridge Plugin Update Checker

About This Script

This bash script automatically checks for Homebridge plugin updates and sends push notifications to your iOS device using the Notify app. It connects to your Homebridge server, authenticates via Config UI X, fetches installed plugins, checks for available updates via NPM registry, and sends notifications for any available updates. Perfect for maintaining your smart home setup! To setup a recoccuring run use CRONTAB or a GUI utility such as Lingon.

#!/bin/bash

# ============================================
# Homebridge Plugin Update Checker
# ============================================
# This script checks for Homebridge plugin updates and sends
# push notifications to your iOS device using the Notify app.
#
# Get the Notify app for iOS and create your notification endpoint at:
# https://notify.pingie.com
#
# No external dependencies required
# Feel free to modify and redistribute - but provide attibution to https://notify.pingie.com/homebridgescript
# ============================================
#
# REQUIREMENTS:
# - macOS with bash, curl, grep, sed (all built-in)
# - Network access to your Homebridge server
# - Internet access to check NPM registry for updates
# - Notify app installed on your iOS device
#
# SETUP INSTRUCTIONS:
# 1. Install the Notify app on your iOS device
# 2. Visit https://notify.pingie.com to get your Device ID and Token
# 3. Update the CONFIGURATION section below with:
#    - Your Notify app credentials (Device ID and Token)
#    - Your Homebridge server URL and login credentials
# 4. Make this script executable: chmod +x check_homebridge_updates.sh
# 5. Run manually or set up a cron job for automatic checks
#
# ============================================

# ============================================
# CONFIGURATION - UPDATE THESE VALUES
# ============================================
# Notify App Settings (for iOS push notifications)
# Get these from https://notify.pingie.com
DEVICE_ID="your-device-id"              # Example: "a1b2c3d4"
TOKEN="your-token"                      # Example: "xyz789token123"

# Homebridge Server Settings
# Your Homebridge Config UI X connection details
# āš ļø  IMPORTANT: NO TRAILING SLASH! āš ļø
# āœ… CORRECT:   http://192.168.1.50:8581
# āŒ INCORRECT: http://192.168.1.50:8581/
HOMEBRIDGE_URL="http://192.168.1.50:8581"   # Your Homebridge IP:Port (NO trailing slash!)
HOMEBRIDGE_USERNAME="admin"                  # Your Config UI X username
HOMEBRIDGE_PASSWORD="your-password"          # Your Config UI X password

# Notification Preferences
BATCH_NOTIFICATIONS=false                # false = individual notifications per plugin
                                         # true = one notification with all updates
NOTIFY_WHEN_NO_UPDATES=false            # false = only notify when updates exist
                                         # true = also notify when everything is up-to-date

# Daily Notification Limit Settings
LIMIT_DAILY_NOTIFICATIONS=true           # true = only notify once per day for each update
                                         # false = notify every time script runs (old behavior)
NOTIFICATION_STATE_FILE="$HOME/.homebridge_update_notifications.state"
                                         # File to track which notifications have been sent today
                                         # Default location is in your home directory

# Output Preferences
MINIMAL_OUTPUT=false                     # false = show detailed progress and results
                                         # true = show only success/failure summary
# ============================================
#
# āš ļø  DO NOT MODIFY ANYTHING BELOW THIS LINE āš ļø
# All configuration should be done above
# ============================================
#
# HOW IT WORKS:
# 1. Connects to your Homebridge Config UI X server
# 2. Authenticates using your credentials
# 3. Fetches the list of installed plugins and versions
# 4. Checks each plugin against NPM registry for updates
# 5. Sends iOS notifications for available updates
#
# PLUGINS CHECKED:
# - All homebridge-* plugins
# - Homebridge core itself
# - Homebridge Config UI X (homebridge-config-ui-x)
# - Scoped plugins (@*/homebridge-*)
#
# ============================================

# Build notification URL from variables
NOTIFY_URL="https://notifyapns.pingie.com/notify-json/${DEVICE_ID}?token=${TOKEN}"
NPM_REGISTRY="https://registry.npmjs.org"

# Authentication token for Homebridge API
AUTH_TOKEN=""

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Arrays to store updates
declare -a UPDATE_PLUGINS
declare -a UPDATE_CURRENT_VERSIONS
declare -a UPDATE_LATEST_VERSIONS

# Function to get today's date in YYYY-MM-DD format
get_today_date() {
    date "+%Y-%m-%d"
}

# Function to initialize or clean notification state file
init_notification_state() {
    if [[ "$LIMIT_DAILY_NOTIFICATIONS" != true ]]; then
        return 0
    fi
    
    local today=$(get_today_date)
    
    # Create state file if it doesn't exist
    if [[ ! -f "$NOTIFICATION_STATE_FILE" ]]; then
        echo "# Homebridge Update Notification State File" > "$NOTIFICATION_STATE_FILE"
        echo "# Format: DATE|PLUGIN|VERSION" >> "$NOTIFICATION_STATE_FILE"
        if [[ "$MINIMAL_OUTPUT" != true ]]; then
            echo "šŸ“ Created notification state file: $NOTIFICATION_STATE_FILE"
        fi
    fi
    
    # Clean up old entries (not from today)
    local temp_file="${NOTIFICATION_STATE_FILE}.tmp"
    echo "# Homebridge Update Notification State File" > "$temp_file"
    echo "# Format: DATE|PLUGIN|VERSION" >> "$temp_file"
    
    while IFS='|' read -r date plugin version; do
        # Skip comments and empty lines
        if [[ "$date" == "#"* ]] || [[ -z "$date" ]]; then
            continue
        fi
        
        # Keep only today's entries
        if [[ "$date" == "$today" ]]; then
            echo "${date}|${plugin}|${version}" >> "$temp_file"
        fi
    done < "$NOTIFICATION_STATE_FILE"
    
    mv "$temp_file" "$NOTIFICATION_STATE_FILE"
}

# Function to check if notification was already sent today
was_notification_sent_today() {
    local plugin_name=$1
    local latest_version=$2
    
    if [[ "$LIMIT_DAILY_NOTIFICATIONS" != true ]]; then
        return 1  # Not sent (allow notification)
    fi
    
    local today=$(get_today_date)
    local search_pattern="${today}|${plugin_name}|${latest_version}"
    
    if grep -q "^${search_pattern}$" "$NOTIFICATION_STATE_FILE" 2>/dev/null; then
        return 0  # Already sent today
    else
        return 1  # Not sent yet
    fi
}

# Function to record that notification was sent
record_notification_sent() {
    local plugin_name=$1
    local latest_version=$2
    
    if [[ "$LIMIT_DAILY_NOTIFICATIONS" != true ]]; then
        return 0
    fi
    
    local today=$(get_today_date)
    echo "${today}|${plugin_name}|${latest_version}" >> "$NOTIFICATION_STATE_FILE"
}

# Function to compare semantic versions
# Returns 0 if version1 < version2, 1 if version1 >= version2
version_lt() {
    local v1=$1
    local v2=$2
    
    # Remove 'v' prefix if present
    v1=${v1#v}
    v2=${v2#v}
    
    # Split versions into arrays
    IFS='.' read -ra V1 <<< "$v1"
    IFS='.' read -ra V2 <<< "$v2"
    
    # Compare each part
    for i in {0..2}; do
        local num1=${V1[$i]:-0}
        local num2=${V2[$i]:-0}
        
        # Remove any non-numeric suffixes (like -beta, -rc)
        num1=${num1%%-*}
        num2=${num2%%-*}
        
        if [[ $num1 -lt $num2 ]]; then
            return 0
        elif [[ $num1 -gt $num2 ]]; then
            return 1
        fi
    done
    
    return 1
}

# Function to get latest version from npm registry
get_latest_version() {
    local package=$1
    local url="${NPM_REGISTRY}/${package}/latest"
    
    # Fetch latest version from npm registry
    local response=$(curl -s "$url" 2>/dev/null)
    
    if [[ $? -eq 0 ]] && [[ ! -z "$response" ]]; then
        # Extract version from JSON response
        echo "$response" | grep -o '"version":"[^"]*"' | cut -d'"' -f4
    else
        echo ""
    fi
}

# Function to authenticate with Homebridge Config UI X
authenticate_homebridge() {
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo "šŸ” Authenticating with Homebridge server..."
    fi
    
    # Create auth payload
    local auth_payload="{\"username\":\"${HOMEBRIDGE_USERNAME}\",\"password\":\"${HOMEBRIDGE_PASSWORD}\"}"
    
    # Send authentication request
    local response=$(curl -s -X POST "${HOMEBRIDGE_URL}/api/auth/login" \
        -H "Content-Type: application/json" \
        -d "$auth_payload" 2>/dev/null)
    
    # Check if response contains access_token
    if [[ "$response" == *"access_token"* ]]; then
        # Extract token using sed
        AUTH_TOKEN=$(echo "$response" | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p')
        
        if [[ ! -z "$AUTH_TOKEN" ]]; then
            if [[ "$MINIMAL_OUTPUT" != true ]]; then
                echo "āœ… Successfully authenticated with Homebridge"
            fi
            return 0
        fi
    fi
    
    echo -e "${RED}āŒ Failed to authenticate with Homebridge server${NC}"
    echo -e "${RED}   Please check your HOMEBRIDGE_URL, USERNAME, and PASSWORD${NC}"
    echo -e "${RED}   URL should have NO trailing slash!${NC}"
    return 1
}

# Function to get plugins from Homebridge server
get_plugins_from_homebridge() {
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo "šŸ“¦ Fetching installed plugins from Homebridge server..."
    fi
    
    # Get plugins list from API
    local response=$(curl -s -X GET "${HOMEBRIDGE_URL}/api/plugins" \
        -H "Authorization: Bearer ${AUTH_TOKEN}" \
        -H "Content-Type: application/json" 2>/dev/null)
    
    # Check if response is valid
    if [[ -z "$response" ]] || [[ "$response" == *"error"* ]] || [[ "$response" == *"statusCode"* ]]; then
        echo -e "${RED}āŒ Failed to fetch plugins from Homebridge server${NC}"
        return 1
    fi
    
    # Parse the JSON response to extract plugin information
    # Extract all plugin entries and format as name@version
    PLUGINS=""
    
    # Use a more robust parsing approach
    local IFS=$'\n'
    local names=($(echo "$response" | grep -o '"name":"[^"]*"' | sed 's/"name":"//g' | sed 's/"//g'))
    local versions=($(echo "$response" | grep -o '"installedVersion":"[^"]*"' | sed 's/"installedVersion":"//g' | sed 's/"//g'))
    
    # Combine names and versions
    local count=${#names[@]}
    for ((i=0; i<$count; i++)); do
        if [[ ! -z "${names[$i]}" ]] && [[ ! -z "${versions[$i]}" ]]; then
            # Include homebridge plugins and homebridge itself
            if [[ "${names[$i]}" == "homebridge" ]] || [[ "${names[$i]}" == homebridge* ]] || [[ "${names[$i]}" == @*/homebridge* ]]; then
                PLUGINS="${PLUGINS}${names[$i]}@${versions[$i]}"$'\n'
            fi
        fi
    done
    
    # Remove trailing newline
    PLUGINS=$(echo "$PLUGINS" | sed '/^$/d')
    
    if [[ -z "$PLUGINS" ]]; then
        echo -e "${YELLOW}No Homebridge plugins found on server${NC}"
        return 1
    fi
    
    local plugin_count=$(echo "$PLUGINS" | grep -c .)
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo "āœ… Found $plugin_count plugin(s) on Homebridge server"
    fi
    
    return 0
}

# Function to send individual notification for a single plugin
send_single_notification() {
    local plugin_name=$1
    local current_version=$2
    local latest_version=$3
    
    # Check if already notified today
    if was_notification_sent_today "$plugin_name" "$latest_version"; then
        if [[ "$MINIMAL_OUTPUT" != true ]]; then
            echo "  ā­ļø  Already notified today (skipping)"
        fi
        return 2  # Special return code for "already notified"
    fi
    
    local message="${plugin_name} has update: ${current_version} → ${latest_version}"
    
    # Create JSON payload
    local json_payload="{\"text\": \"$message\"}"
    
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo "  šŸ“¤ Sending notification for: $plugin_name"
    fi
    
    # Send POST request
    local response=$(curl -s -w "\n%{http_code}" -X POST "$NOTIFY_URL" \
        -H "Content-Type: application/json" \
        -d "$json_payload" 2>/dev/null)
    
    local http_code=$(echo "$response" | tail -n1)
    
    if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]] || [[ "$http_code" == "204" ]]; then
        if [[ "$MINIMAL_OUTPUT" != true ]]; then
            echo "  āœ… Notification sent successfully"
        fi
        record_notification_sent "$plugin_name" "$latest_version"
        return 0
    else
        echo "  āŒ Failed to send notification (HTTP $http_code)"
        return 1
    fi
}

# Function to send batch notification for multiple plugins
send_batch_notification() {
    local update_count=$1
    
    if [[ $update_count -eq 0 ]]; then
        return 0
    fi
    
    # Filter out already notified updates if daily limit is enabled
    local new_updates_count=0
    local new_update_list=""
    
    for i in "${!UPDATE_PLUGINS[@]}"; do
        if ! was_notification_sent_today "${UPDATE_PLUGINS[$i]}" "${UPDATE_LATEST_VERSIONS[$i]}"; then
            new_updates_count=$((new_updates_count + 1))
            if [[ -z "$new_update_list" ]]; then
                new_update_list="• ${UPDATE_PLUGINS[$i]}: ${UPDATE_CURRENT_VERSIONS[$i]} → ${UPDATE_LATEST_VERSIONS[$i]}"
            else
                new_update_list="${new_update_list}\n• ${UPDATE_PLUGINS[$i]}: ${UPDATE_CURRENT_VERSIONS[$i]} → ${UPDATE_LATEST_VERSIONS[$i]}"
            fi
        fi
    done
    
    # If no new updates to notify about, skip
    if [[ $new_updates_count -eq 0 ]]; then
        if [[ "$MINIMAL_OUTPUT" != true ]]; then
            echo ""
            echo "ā­ļø  All updates already notified today (skipping batch notification)"
        fi
        return 2
    fi
    
    local message="Homebridge: ${new_updates_count} plugin update"
    if [[ $new_updates_count -gt 1 ]]; then
        message="${message}s"
    fi
    message="${message} available:"
    message="${message}\n${new_update_list}"
    
    # Create JSON payload
    local json_payload=$(printf '{"text": "%s"}' "$message")
    
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo ""
        echo "šŸ“¤ Sending batch notification for $new_updates_count new update(s)..."
    fi
    
    # Send POST request
    local response=$(curl -s -w "\n%{http_code}" -X POST "$NOTIFY_URL" \
        -H "Content-Type: application/json" \
        -d "$json_payload" 2>/dev/null)
    
    local http_code=$(echo "$response" | tail -n1)
    
    if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]] || [[ "$http_code" == "204" ]]; then
        if [[ "$MINIMAL_OUTPUT" != true ]]; then
            echo "āœ… Batch notification sent successfully"
        fi
        # Record all notifications as sent
        for i in "${!UPDATE_PLUGINS[@]}"; do
            if ! was_notification_sent_today "${UPDATE_PLUGINS[$i]}" "${UPDATE_LATEST_VERSIONS[$i]}"; then
                record_notification_sent "${UPDATE_PLUGINS[$i]}" "${UPDATE_LATEST_VERSIONS[$i]}"
            fi
        done
        return 0
    else
        echo "āŒ Failed to send batch notification (HTTP $http_code)"
        return 1
    fi
}

# Function to validate configuration
validate_config() {
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo -e "${BLUE}šŸ“± Using Notify app for iOS notifications${NC}"
    fi
    
    # Validate Notify app settings
    if [[ "$DEVICE_ID" == "your-device-id" ]]; then
        echo -e "${RED}āš ļø  Error: DEVICE_ID not configured.${NC}"
        echo -e "${RED}   Please update with your device ID from the Notify app.${NC}"
        echo -e "${RED}   Get it at: https://notify.pingie.com${NC}"
        echo ""
        exit 1
    fi
    
    if [[ "$TOKEN" == "your-token" ]]; then
        echo -e "${RED}āš ļø  Error: TOKEN not configured.${NC}"
        echo -e "${RED}   Please update with your token from the Notify app.${NC}"
        echo -e "${RED}   Get it at: https://notify.pingie.com${NC}"
        echo ""
        exit 1
    fi
    
    # Validate Homebridge settings
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo -e "${BLUE}šŸ  Homebridge Server: ${HOMEBRIDGE_URL}${NC}"
    fi
    
    if [[ "$HOMEBRIDGE_PASSWORD" == "your-password" ]]; then
        echo -e "${RED}āš ļø  Error: HOMEBRIDGE_PASSWORD not configured.${NC}"
        echo -e "${RED}   Please update with your Homebridge Config UI X password.${NC}"
        echo ""
        exit 1
    fi
    
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo -e "${BLUE}šŸ“” Notification URL: https://notify.pingie.com/notify-json/${DEVICE_ID}${NC}"
        if [[ "$LIMIT_DAILY_NOTIFICATIONS" == true ]]; then
            echo -e "${BLUE}šŸ”” Daily notification limit: ENABLED${NC}"
            echo -e "${BLUE}šŸ“„ State file: ${NOTIFICATION_STATE_FILE}${NC}"
        else
            echo -e "${BLUE}šŸ”” Daily notification limit: DISABLED${NC}"
        fi
        echo ""
    fi
}

# Main script
main() {
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo "=========================================="
        echo "  Homebridge Plugin Update Checker"
        echo "  with iOS Notifications via Notify App"
        echo "=========================================="
        echo ""
    fi
    
    # Validate configuration
    validate_config
    
    # Initialize notification state tracking
    init_notification_state
    
    # Connect to Homebridge server
    if ! authenticate_homebridge; then
        echo -e "${RED}āŒ Could not connect to Homebridge server${NC}"
        echo "Please check your HOMEBRIDGE_URL, USERNAME, and PASSWORD"
        echo "URL must have NO trailing slash!"
        exit 1
    fi
    
    # Get plugins from Homebridge server
    if ! get_plugins_from_homebridge; then
        echo -e "${RED}āŒ Unable to fetch plugin list from Homebridge${NC}"
        exit 1
    fi
    
    # Count total plugins
    TOTAL_PLUGINS=$(echo "$PLUGINS" | grep -c .)
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo ""
        echo "šŸ“¦ Total plugins to check: $TOTAL_PLUGINS"
        echo ""
        echo "Checking for updates..."
        echo "----------------------------------------"
    fi
    
    UPDATE_COUNT=0
    SKIPPED_COUNT=0
    NOTIFIED_COUNT=0
    UPDATE_LIST=""
    
    # Process each plugin
    while IFS= read -r plugin_line; do
        # Skip empty lines
        [[ -z "$plugin_line" ]] && continue
        
        # Extract plugin name and version
        if [[ "$plugin_line" == *"@"* ]]; then
            plugin_name=$(echo "$plugin_line" | rev | cut -d'@' -f2- | rev)
            current_version=$(echo "$plugin_line" | rev | cut -d'@' -f1 | rev)
            
            # Clean up version string
            current_version=$(echo "$current_version" | sed 's/[^0-9.]//g')
        else
            # Skip if no version info
            continue
        fi
        
        # Skip if we couldn't get valid data
        if [[ -z "$plugin_name" ]] || [[ -z "$current_version" ]]; then
            continue
        fi
        
        if [[ "$MINIMAL_OUTPUT" != true ]]; then
            echo -n "šŸ“¦ $plugin_name ($current_version) ... "
        fi
        
        # Get latest version from npm registry
        latest_version=$(get_latest_version "$plugin_name")
        
        if [[ -z "$latest_version" ]]; then
            if [[ "$MINIMAL_OUTPUT" != true ]]; then
                echo -e "${YELLOW}Could not fetch latest version${NC}"
            fi
            continue
        fi
        
        # Compare versions
        if version_lt "$current_version" "$latest_version"; then
            if [[ "$MINIMAL_OUTPUT" != true ]]; then
                echo -e "${YELLOW}Update available → $latest_version${NC}"
            fi
            
            # Store update information
            UPDATE_PLUGINS+=("$plugin_name")
            UPDATE_CURRENT_VERSIONS+=("$current_version")
            UPDATE_LATEST_VERSIONS+=("$latest_version")
            
            UPDATE_COUNT=$((UPDATE_COUNT + 1))
            UPDATE_LIST="${UPDATE_LIST}\n  • ${plugin_name}: ${current_version} → ${latest_version}"
            
            # Send individual notification if batch mode is disabled
            if [[ "$BATCH_NOTIFICATIONS" == false ]]; then
                send_single_notification "$plugin_name" "$current_version" "$latest_version"
                local result=$?
                if [[ $result -eq 0 ]]; then
                    NOTIFIED_COUNT=$((NOTIFIED_COUNT + 1))
                elif [[ $result -eq 2 ]]; then
                    SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
                fi
            fi
        else
            if [[ "$MINIMAL_OUTPUT" != true ]]; then
                echo -e "${GREEN}Up to date${NC}"
            fi
        fi
        
    done <<< "$PLUGINS"
    
    # Send batch notification if enabled and there are updates
    if [[ "$BATCH_NOTIFICATIONS" == true ]] && [[ $UPDATE_COUNT -gt 0 ]]; then
        send_batch_notification $UPDATE_COUNT
        local result=$?
        if [[ $result -eq 0 ]]; then
            NOTIFIED_COUNT=1  # One batch notification sent
        elif [[ $result -eq 2 ]]; then
            SKIPPED_COUNT=$UPDATE_COUNT  # All were already notified
        fi
    fi
    
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo ""
        echo "----------------------------------------"
    fi
    
    if [[ $UPDATE_COUNT -gt 0 ]]; then
        if [[ "$MINIMAL_OUTPUT" == true ]]; then
            # Minimal output - just show summary
            echo "āœ… Checked $TOTAL_PLUGINS plugins: $UPDATE_COUNT update(s) available"
            if [[ "$LIMIT_DAILY_NOTIFICATIONS" == true ]] && [[ $SKIPPED_COUNT -gt 0 ]]; then
                echo "ā­ļø  Skipped $SKIPPED_COUNT notification(s) (already sent today)"
            fi
            if [[ "$BATCH_NOTIFICATIONS" == true ]]; then
                if [[ $NOTIFIED_COUNT -gt 0 ]]; then
                    echo "šŸ“¬ Sent 1 batch notification"
                fi
            else
                if [[ $NOTIFIED_COUNT -gt 0 ]]; then
                    echo "šŸ“¬ Sent $NOTIFIED_COUNT notification(s)"
                fi
            fi
        else
            # Full output
            echo -e "${YELLOW}šŸ“‹ Summary: $UPDATE_COUNT update(s) available${NC}"
            echo -e "$UPDATE_LIST"
            echo ""
            
            # Show notification status
            if [[ "$BATCH_NOTIFICATIONS" == true ]]; then
                if [[ $NOTIFIED_COUNT -gt 0 ]]; then
                    echo "šŸ“¬ Sent 1 batch notification for new updates"
                fi
                if [[ "$LIMIT_DAILY_NOTIFICATIONS" == true ]] && [[ $SKIPPED_COUNT -gt 0 ]]; then
                    echo "ā­ļø  Skipped notification for $SKIPPED_COUNT update(s) already notified today"
                fi
            else
                if [[ $NOTIFIED_COUNT -gt 0 ]]; then
                    echo "šŸ“¬ Sent $NOTIFIED_COUNT individual notification(s)"
                fi
                if [[ "$LIMIT_DAILY_NOTIFICATIONS" == true ]] && [[ $SKIPPED_COUNT -gt 0 ]]; then
                    echo "ā­ļø  Skipped $SKIPPED_COUNT notification(s) already sent today"
                fi
            fi
            echo ""
            
            echo "To update all plugins, run:"
            echo "  npm update -g"
            echo ""
            echo "To update a specific plugin, run:"
            echo "  npm install -g @latest"
        fi
    else
        if [[ "$MINIMAL_OUTPUT" == true ]]; then
            # Minimal output for all up-to-date
            echo "āœ… Checked $TOTAL_PLUGINS plugins: All up to date"
        else
            echo -e "${GREEN}āœ… All plugins are up to date!${NC}"
        fi
        
        # Send notification if configured to notify when no updates
        if [[ "$NOTIFY_WHEN_NO_UPDATES" == true ]]; then
            # Check if already sent today (for "all clear" notifications)
            local all_clear_key="ALL_PLUGINS_UP_TO_DATE"
            local today=$(get_today_date)
            
            if [[ "$LIMIT_DAILY_NOTIFICATIONS" == true ]] && was_notification_sent_today "$all_clear_key" "$today"; then
                if [[ "$MINIMAL_OUTPUT" != true ]]; then
                    echo ""
                    echo "ā­ļø  All-clear notification already sent today"
                    echo "šŸ“­ No new notifications sent"
                fi
            else
                if [[ "$MINIMAL_OUTPUT" != true ]]; then
                    echo ""
                    echo "šŸ“¤ Sending all-clear notification..."
                fi
                
                # Get current time for the notification
                local current_time=$(date "+%I:%M %p")
                local message="Homebridge check at ${current_time}: All $TOTAL_PLUGINS plugins are up to date āœ…"
                local json_payload="{\"text\": \"$message\"}"
                
                local response=$(curl -s -w "\n%{http_code}" -X POST "$NOTIFY_URL" \
                    -H "Content-Type: application/json" \
                    -d "$json_payload" 2>/dev/null)
                
                local http_code=$(echo "$response" | tail -n1)
                
                if [[ "$http_code" == "200" ]] || [[ "$http_code" == "201" ]] || [[ "$http_code" == "204" ]]; then
                    if [[ "$LIMIT_DAILY_NOTIFICATIONS" == true ]]; then
                        record_notification_sent "$all_clear_key" "$today"
                    fi
                    if [[ "$MINIMAL_OUTPUT" == true ]]; then
                        echo "šŸ“¬ Sent all-clear notification"
                    else
                        echo "āœ… All-clear notification sent"
                        echo "šŸ“¬ Sent 1 notification"
                    fi
                else
                    echo "āŒ Failed to send all-clear notification (HTTP $http_code)"
                    if [[ "$MINIMAL_OUTPUT" != true ]]; then
                        echo "šŸ“­ No notifications sent"
                    fi
                fi
            fi
        else
            if [[ "$MINIMAL_OUTPUT" != true ]]; then
                echo "šŸ“­ No notifications sent (all up to date)"
                echo ""
                echo -e "${BLUE}šŸ’” Tip: Set NOTIFY_WHEN_NO_UPDATES=true to get notified${NC}"
                echo -e "${BLUE}   even when all plugins are up to date.${NC}"
            fi
        fi
    fi
    
    if [[ "$MINIMAL_OUTPUT" != true ]]; then
        echo ""
        echo "=========================================="
    fi
}

# Run main function
main
āœ“ Script copied to clipboard!