Bluetooth Management on macOS
Control and manage Bluetooth settings across your MacFleet devices using advanced command-line tools. This tutorial covers Bluetooth status monitoring, power state management, and enterprise-grade fleet control with comprehensive logging and security features.
Understanding macOS Bluetooth Management
macOS provides comprehensive Bluetooth control through several mechanisms:
defaults
- Direct manipulation of Bluetooth preferences via plist filesbluetoothd
- The core Bluetooth daemon that handles all Bluetooth operationslaunchctl
- Service management for starting/stopping Bluetooth servicesblued
- Legacy Bluetooth daemon name used in older macOS versions
The primary configuration file is located at /Library/Preferences/com.apple.Bluetooth
with the ControllerPowerState
key controlling the power state.
Basic Bluetooth Status Management
Check Bluetooth Status
#!/bin/bash
# Check current Bluetooth power state
check_bluetooth_status() {
local status
status=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
if [[ "$status" == "1" ]]; then
echo "Bluetooth is ON"
return 0
elif [[ "$status" == "0" ]]; then
echo "Bluetooth is OFF"
return 1
else
echo "Bluetooth status unknown or not configured"
return 2
fi
}
# Execute status check
check_bluetooth_status
Turn Bluetooth ON
#!/bin/bash
# Turn Bluetooth ON using defaults and daemon restart
turn_bluetooth_on() {
echo "Turning Bluetooth ON..."
# Set Bluetooth power state to ON
sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 1
# Restart Bluetooth daemon (try both modern and legacy daemon names)
if pgrep bluetoothd >/dev/null 2>&1; then
sudo killall -HUP bluetoothd
echo "Bluetooth daemon (bluetoothd) restarted"
elif pgrep blued >/dev/null 2>&1; then
sudo killall -HUP blued
echo "Bluetooth daemon (blued) restarted"
else
echo "Warning: Bluetooth daemon not found"
fi
# Wait for changes to take effect
sleep 3
# Verify the change
if check_bluetooth_status >/dev/null; then
echo "✅ Bluetooth successfully turned ON"
return 0
else
echo "❌ Failed to turn Bluetooth ON"
return 1
fi
}
# Helper function for status checking
check_bluetooth_status() {
local status
status=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
[[ "$status" == "1" ]]
}
# Execute
turn_bluetooth_on
Turn Bluetooth OFF
#!/bin/bash
# Turn Bluetooth OFF using defaults and daemon restart
turn_bluetooth_off() {
echo "Turning Bluetooth OFF..."
# Set Bluetooth power state to OFF
sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 0
# Restart Bluetooth daemon
if pgrep bluetoothd >/dev/null 2>&1; then
sudo killall -HUP bluetoothd
echo "Bluetooth daemon (bluetoothd) restarted"
elif pgrep blued >/dev/null 2>&1; then
sudo killall -HUP blued
echo "Bluetooth daemon (blued) restarted"
else
echo "Warning: Bluetooth daemon not found"
fi
# Wait for changes to take effect
sleep 3
# Verify the change
if ! check_bluetooth_status >/dev/null; then
echo "✅ Bluetooth successfully turned OFF"
return 0
else
echo "❌ Failed to turn Bluetooth OFF"
return 1
fi
}
# Helper function for status checking
check_bluetooth_status() {
local status
status=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
[[ "$status" == "1" ]]
}
# Execute
turn_bluetooth_off
Advanced Bluetooth Management with launchctl
Using launchctl for Service Management
#!/bin/bash
# Advanced Bluetooth control using launchctl service management
bluetooth_service_control() {
local action="$1" # on, off, restart, status
local daemon_name
# Determine the correct daemon name based on macOS version
if launchctl list | grep -q "com.apple.bluetoothd"; then
daemon_name="com.apple.bluetoothd"
elif launchctl list | grep -q "com.apple.blued"; then
daemon_name="com.apple.blued"
else
echo "❌ Bluetooth daemon not found in launchctl"
return 1
fi
case "$action" in
"on")
echo "Enabling Bluetooth..."
sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 1
sudo launchctl stop "$daemon_name"
sleep 2
sudo launchctl start "$daemon_name"
sleep 3
echo "✅ Bluetooth enabled using launchctl"
;;
"off")
echo "Disabling Bluetooth..."
sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 0
sudo launchctl stop "$daemon_name"
sleep 2
sudo launchctl start "$daemon_name"
sleep 3
echo "✅ Bluetooth disabled using launchctl"
;;
"restart")
echo "Restarting Bluetooth service..."
sudo launchctl stop "$daemon_name"
sleep 2
sudo launchctl start "$daemon_name"
sleep 3
echo "✅ Bluetooth service restarted"
;;
"status")
echo "Bluetooth daemon status: $daemon_name"
if launchctl list | grep -q "$daemon_name"; then
echo "✅ Bluetooth service is running"
# Check power state
local power_state
power_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
echo "Power state: $([[ "$power_state" == "1" ]] && echo "ON" || echo "OFF")"
else
echo "❌ Bluetooth service is not running"
fi
;;
*)
echo "Usage: bluetooth_service_control {on|off|restart|status}"
return 1
;;
esac
}
# Examples
bluetooth_service_control "status"
# bluetooth_service_control "on"
# bluetooth_service_control "off"
# bluetooth_service_control "restart"
Enterprise Bluetooth Management System
#!/bin/bash
# MacFleet Enterprise Bluetooth Management System
# Comprehensive Bluetooth control with logging, monitoring, and fleet management
# Configuration
LOG_FILE="/var/log/macfleet_bluetooth.log"
CONFIG_FILE="/etc/macfleet/bluetooth_config.conf"
BACKUP_DIR="/var/backups/macfleet/bluetooth"
API_ENDPOINT="https://api.macfleet.com/v1/bluetooth"
# Create directory structure
setup_directories() {
mkdir -p "$(dirname "$LOG_FILE")" "$BACKUP_DIR" "$(dirname "$CONFIG_FILE")"
touch "$LOG_FILE"
}
# Logging function
log_action() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Get detailed Bluetooth information
get_bluetooth_info() {
local info_json="{"
# Power state
local power_state
power_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null || echo "unknown")
info_json+="\"power_state\": \"$power_state\","
# Daemon status
local daemon_running=false
if pgrep bluetoothd >/dev/null 2>&1 || pgrep blued >/dev/null 2>&1; then
daemon_running=true
fi
info_json+="\"daemon_running\": $daemon_running,"
# macOS version
local macos_version
macos_version=$(sw_vers -productVersion)
info_json+="\"macos_version\": \"$macos_version\","
# System hardware
local hardware_model
hardware_model=$(system_profiler SPHardwareDataType | grep "Model Name" | awk -F': ' '{print $2}' | xargs)
info_json+="\"hardware_model\": \"$hardware_model\","
# Bluetooth controller info
local bt_controller=""
if system_profiler SPBluetoothDataType >/dev/null 2>&1; then
bt_controller=$(system_profiler SPBluetoothDataType | grep -A5 "Apple Bluetooth Software" | grep "Version" | awk -F': ' '{print $2}' | xargs)
fi
info_json+="\"bluetooth_version\": \"$bt_controller\","
# Timestamp
info_json+="\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\""
info_json+="}"
echo "$info_json"
}
# Backup current Bluetooth configuration
backup_bluetooth_config() {
local backup_file="$BACKUP_DIR/bluetooth_backup_$(date +%Y%m%d_%H%M%S).plist"
if [[ -f "/Library/Preferences/com.apple.Bluetooth.plist" ]]; then
cp "/Library/Preferences/com.apple.Bluetooth.plist" "$backup_file"
log_action "Bluetooth configuration backed up to: $backup_file"
echo "$backup_file"
else
log_action "WARNING: Bluetooth preference file not found for backup"
return 1
fi
}
# Set Bluetooth state with comprehensive validation
set_bluetooth_state() {
local desired_state="$1" # "on" or "off"
local force_restart="${2:-false}"
# Validate input
if [[ "$desired_state" != "on" && "$desired_state" != "off" ]]; then
log_action "ERROR: Invalid state specified. Use 'on' or 'off'"
return 1
fi
# Get current state
local current_state
current_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
local target_value
if [[ "$desired_state" == "on" ]]; then
target_value=1
else
target_value=0
fi
# Check if change is needed
if [[ "$current_state" == "$target_value" && "$force_restart" == "false" ]]; then
log_action "Bluetooth already in desired state: $desired_state"
return 0
fi
# Create backup before changes
backup_bluetooth_config
# Set new state
log_action "Setting Bluetooth state to: $desired_state"
sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int "$target_value"
# Restart Bluetooth daemon
restart_bluetooth_daemon
# Wait for changes to propagate
sleep 5
# Verify the change
local new_state
new_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
if [[ "$new_state" == "$target_value" ]]; then
log_action "✅ Bluetooth successfully set to: $desired_state"
return 0
else
log_action "❌ Failed to set Bluetooth to: $desired_state"
return 1
fi
}
# Restart Bluetooth daemon with error handling
restart_bluetooth_daemon() {
log_action "Restarting Bluetooth daemon..."
# Try modern bluetoothd first
if pgrep bluetoothd >/dev/null 2>&1; then
if sudo killall -HUP bluetoothd 2>/dev/null; then
log_action "Bluetooth daemon (bluetoothd) restarted successfully"
return 0
else
log_action "WARNING: Failed to restart bluetoothd with HUP signal"
fi
fi
# Try legacy blued
if pgrep blued >/dev/null 2>&1; then
if sudo killall -HUP blued 2>/dev/null; then
log_action "Bluetooth daemon (blued) restarted successfully"
return 0
else
log_action "WARNING: Failed to restart blued with HUP signal"
fi
fi
# Try launchctl approach
local daemon_service=""
if launchctl list | grep -q "com.apple.bluetoothd"; then
daemon_service="com.apple.bluetoothd"
elif launchctl list | grep -q "com.apple.blued"; then
daemon_service="com.apple.blued"
fi
if [[ -n "$daemon_service" ]]; then
log_action "Attempting launchctl restart for: $daemon_service"
sudo launchctl stop "$daemon_service" 2>/dev/null
sleep 2
sudo launchctl start "$daemon_service" 2>/dev/null
log_action "Bluetooth service restarted via launchctl"
return 0
fi
log_action "ERROR: Could not restart Bluetooth daemon"
return 1
}
# Monitor Bluetooth connectivity and paired devices
monitor_bluetooth_devices() {
log_action "Starting Bluetooth device monitoring..."
# Get current power state
local power_state
power_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
if [[ "$power_state" != "1" ]]; then
log_action "Bluetooth is OFF - no devices to monitor"
return 0
fi
# Check for system_profiler availability
if ! command -v system_profiler >/dev/null 2>&1; then
log_action "WARNING: system_profiler not available for device monitoring"
return 1
fi
# Get Bluetooth device information
local bt_devices
bt_devices=$(system_profiler SPBluetoothDataType 2>/dev/null | grep -A10 "Devices:" || echo "No devices found")
log_action "Bluetooth devices status:"
echo "$bt_devices" | while IFS= read -r line; do
log_action " $line"
done
# Count connected devices
local connected_count
connected_count=$(echo "$bt_devices" | grep -c "Connected: Yes" || echo "0")
log_action "Total connected Bluetooth devices: $connected_count"
return 0
}
# Generate comprehensive Bluetooth report
generate_bluetooth_report() {
local report_file="/tmp/bluetooth_report_$(date +%Y%m%d_%H%M%S).json"
log_action "Generating Bluetooth report: $report_file"
{
echo "{"
echo " \"report_type\": \"bluetooth_status\","
echo " \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\","
echo " \"hostname\": \"$(hostname)\","
echo " \"device_info\": $(get_bluetooth_info),"
# System information
echo " \"system_info\": {"
echo " \"macos_version\": \"$(sw_vers -productVersion)\","
echo " \"build_version\": \"$(sw_vers -buildVersion)\","
echo " \"hardware_uuid\": \"$(system_profiler SPHardwareDataType | grep 'Hardware UUID' | awk -F': ' '{print $2}' | xargs)\""
echo " },"
# Bluetooth preferences
echo " \"bluetooth_preferences\": {"
if [[ -f "/Library/Preferences/com.apple.Bluetooth.plist" ]]; then
echo " \"preferences_file_exists\": true,"
echo " \"file_size\": $(stat -f%z "/Library/Preferences/com.apple.Bluetooth.plist"),"
echo " \"last_modified\": \"$(stat -f%Sm -t%Y-%m-%dT%H:%M:%SZ "/Library/Preferences/com.apple.Bluetooth.plist")\""
else
echo " \"preferences_file_exists\": false"
fi
echo " }"
echo "}"
} > "$report_file"
log_action "Bluetooth report generated successfully"
echo "$report_file"
}
# Security compliance check
check_bluetooth_security() {
log_action "Performing Bluetooth security compliance check..."
local security_issues=()
# Check if Bluetooth is enabled when it should be disabled
if [[ -f "$CONFIG_FILE" ]] && grep -q "BLUETOOTH_POLICY=disabled" "$CONFIG_FILE"; then
local current_state
current_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
if [[ "$current_state" == "1" ]]; then
security_issues+=("Bluetooth enabled despite policy requiring disabled state")
fi
fi
# Check for Bluetooth discoverability (if system_profiler available)
if command -v system_profiler >/dev/null 2>&1; then
local discoverable
discoverable=$(system_profiler SPBluetoothDataType 2>/dev/null | grep -i "discoverable" || echo "unknown")
if echo "$discoverable" | grep -qi "yes"; then
security_issues+=("Bluetooth is discoverable - potential security risk")
fi
fi
# Report security status
if [[ ${#security_issues[@]} -eq 0 ]]; then
log_action "✅ Bluetooth security compliance check passed"
return 0
else
log_action "⚠️ Bluetooth security issues found:"
for issue in "${security_issues[@]}"; do
log_action " - $issue"
done
return 1
fi
}
# Main management function
main() {
local action="${1:-status}"
local parameter="$2"
setup_directories
log_action "MacFleet Bluetooth Management started with action: $action"
case "$action" in
"on"|"enable")
set_bluetooth_state "on"
;;
"off"|"disable")
set_bluetooth_state "off"
;;
"restart"|"reload")
restart_bluetooth_daemon
;;
"status"|"info")
get_bluetooth_info | python3 -m json.tool 2>/dev/null || get_bluetooth_info
;;
"monitor")
monitor_bluetooth_devices
;;
"report")
generate_bluetooth_report
;;
"security")
check_bluetooth_security
;;
"backup")
backup_bluetooth_config
;;
"force-on")
set_bluetooth_state "on" true
;;
"force-off")
set_bluetooth_state "off" true
;;
*)
echo "Usage: $0 {on|off|restart|status|monitor|report|security|backup|force-on|force-off}"
echo ""
echo "Commands:"
echo " on/enable - Turn Bluetooth ON"
echo " off/disable - Turn Bluetooth OFF"
echo " restart - Restart Bluetooth daemon"
echo " status - Show detailed Bluetooth status"
echo " monitor - Monitor connected devices"
echo " report - Generate comprehensive report"
echo " security - Run security compliance check"
echo " backup - Backup current configuration"
echo " force-on - Force Bluetooth ON (restart daemon)"
echo " force-off - Force Bluetooth OFF (restart daemon)"
exit 1
;;
esac
log_action "MacFleet Bluetooth Management completed with action: $action"
}
# Execute main function with all arguments
main "$@"
Quick Management Functions
Simple Status Check
#!/bin/bash
# Quick Bluetooth status check with color output
bluetooth_quick_status() {
local status
status=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
case "$status" in
"1")
echo "🔵 Bluetooth: ON"
;;
"0")
echo "⚪ Bluetooth: OFF"
;;
*)
echo "❓ Bluetooth: UNKNOWN"
;;
esac
}
bluetooth_quick_status
Toggle Bluetooth State
#!/bin/bash
# Toggle Bluetooth between ON and OFF states
toggle_bluetooth() {
local current_state
current_state=$(defaults read /Library/Preferences/com.apple.Bluetooth ControllerPowerState 2>/dev/null)
if [[ "$current_state" == "1" ]]; then
echo "Bluetooth is ON, turning OFF..."
sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 0
sudo killall -HUP bluetoothd 2>/dev/null || sudo killall -HUP blued 2>/dev/null
echo "✅ Bluetooth turned OFF"
else
echo "Bluetooth is OFF, turning ON..."
sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 1
sudo killall -HUP bluetoothd 2>/dev/null || sudo killall -HUP blued 2>/dev/null
echo "✅ Bluetooth turned ON"
fi
}
toggle_bluetooth
Configuration Templates
Bluetooth Policy Configuration
# /etc/macfleet/bluetooth_config.conf
# MacFleet Bluetooth Management Configuration
# Policy settings
BLUETOOTH_POLICY="enabled" # enabled, disabled, user_controlled
AUTO_DISABLE_ON_IDLE="false"
IDLE_TIMEOUT_MINUTES="30"
# Security settings
ALLOW_DISCOVERABLE="false"
REQUIRE_PAIRING_CONFIRMATION="true"
LOG_DEVICE_CONNECTIONS="true"
# Monitoring settings
MONITOR_INTERVAL_SECONDS="300"
ALERT_ON_NEW_DEVICES="true"
COMPLIANCE_CHECK_ENABLED="true"
# Backup settings
AUTO_BACKUP_ENABLED="true"
BACKUP_RETENTION_DAYS="30"
Troubleshooting Common Issues
Bluetooth Daemon Not Responding
#!/bin/bash
# Troubleshoot and fix Bluetooth daemon issues
fix_bluetooth_daemon() {
echo "Diagnosing Bluetooth daemon issues..."
# Check if daemon is running
if ! pgrep bluetoothd >/dev/null 2>&1 && ! pgrep blued >/dev/null 2>&1; then
echo "❌ No Bluetooth daemon found running"
# Try to start via launchctl
if launchctl list | grep -q "com.apple.bluetoothd"; then
echo "Starting bluetoothd via launchctl..."
sudo launchctl start com.apple.bluetoothd
elif launchctl list | grep -q "com.apple.blued"; then
echo "Starting blued via launchctl..."
sudo launchctl start com.apple.blued
else
echo "❌ Bluetooth service not found in launchctl"
return 1
fi
fi
# Force restart if still having issues
echo "Force restarting Bluetooth system..."
sudo pkill -f bluetooth
sleep 3
# Restart via launchctl
for service in "com.apple.bluetoothd" "com.apple.blued"; do
if launchctl list | grep -q "$service"; then
sudo launchctl stop "$service" 2>/dev/null
sleep 2
sudo launchctl start "$service" 2>/dev/null
echo "Restarted service: $service"
fi
done
# Verify recovery
sleep 5
if pgrep bluetoothd >/dev/null 2>&1 || pgrep blued >/dev/null 2>&1; then
echo "✅ Bluetooth daemon recovery successful"
return 0
else
echo "❌ Bluetooth daemon recovery failed"
return 1
fi
}
fix_bluetooth_daemon
Reset Bluetooth Configuration
#!/bin/bash
# Reset Bluetooth to factory defaults
reset_bluetooth_config() {
echo "⚠️ Resetting Bluetooth configuration to defaults..."
# Create backup
if [[ -f "/Library/Preferences/com.apple.Bluetooth.plist" ]]; then
cp "/Library/Preferences/com.apple.Bluetooth.plist" "/tmp/bluetooth_backup_$(date +%s).plist"
echo "Backup created in /tmp/"
fi
# Stop Bluetooth services
sudo launchctl stop com.apple.bluetoothd 2>/dev/null
sudo launchctl stop com.apple.blued 2>/dev/null
# Remove configuration files
sudo rm -f "/Library/Preferences/com.apple.Bluetooth.plist"
sudo rm -f "/Library/Preferences/ByHost/com.apple.Bluetooth.*.plist"
# Restart services
sudo launchctl start com.apple.bluetoothd 2>/dev/null
sudo launchctl start com.apple.blued 2>/dev/null
# Set default state
sleep 3
sudo defaults write /Library/Preferences/com.apple.Bluetooth ControllerPowerState -int 1
sudo killall -HUP bluetoothd 2>/dev/null || sudo killall -HUP blued 2>/dev/null
echo "✅ Bluetooth configuration reset completed"
}
# Uncomment to execute
# reset_bluetooth_config
Important Technical Notes
Daemon Management
- bluetoothd: Modern Bluetooth daemon (macOS 10.10+)
- blued: Legacy Bluetooth daemon (older macOS versions)
- killall -HUP: Sends hangup signal to restart daemon gracefully
- launchctl: Preferred method for service management
Configuration Details
- File Location:
/Library/Preferences/com.apple.Bluetooth
- Power State Key:
ControllerPowerState
- Values:
1
(ON),0
(OFF) - Persistence: Changes persist across reboots
Security Considerations
- Administrative Privileges: All Bluetooth modifications require
sudo
- Daemon Restart: Changes only take effect after daemon restart
- User Impact: Turning off Bluetooth disconnects all paired devices
- Policy Compliance: Monitor for unauthorized Bluetooth changes
Best Practices
- Always backup configurations before making changes
- Test daemon restart methods for your macOS version
- Monitor for delays - daemon restart can take several seconds
- Implement logging for audit trails and troubleshooting
- Use launchctl for more reliable service management
- Validate changes after applying modifications
- Consider user impact when disabling Bluetooth remotely
- Test scripts thoroughly before fleet deployment
Remember to validate all scripts on test devices before deploying across your MacFleet environment, and be aware that Bluetooth changes may take a few minutes to fully propagate through the system.