Skip to content

Latest commit

Β 

History

History
1364 lines (1089 loc) Β· 33 KB

TROUBLESHOOTING.md

File metadata and controls

1364 lines (1089 loc) Β· 33 KB

Troubleshooting Guide

Comprehensive troubleshooting guide for Eight Sleep smart bed controller issues, solutions, and diagnostics.

Table of Contents

Quick Diagnostics

System Health Check

Run this comprehensive health check to identify common issues:

#!/bin/bash
# health-check.sh

echo "πŸ₯ Eight Sleep Controller Health Check"
echo "======================================"

# Check Node.js/Bun
echo "πŸ“¦ Runtime Environment:"
if command -v bun &> /dev/null; then
    echo "βœ… Bun: $(bun --version)"
else
    echo "❌ Bun not installed"
fi

if command -v node &> /dev/null; then
    echo "βœ… Node.js: $(node --version)"
else
    echo "❌ Node.js not found"
fi

# Check dependencies
echo -e "\nπŸ“š Dependencies:"
if [ -f "package.json" ]; then
    echo "βœ… package.json exists"
    if [ -d "node_modules" ]; then
        echo "βœ… node_modules directory exists"
    else
        echo "❌ Dependencies not installed (run: bun install)"
    fi
else
    echo "❌ package.json not found"
fi

# Check environment file
echo -e "\nπŸ” Configuration:"
if [ -f ".env" ]; then
    echo "βœ… .env file exists"
    
    # Check required variables (without exposing values)
    if grep -q "EIGHT_SLEEP_EMAIL=" .env; then
        echo "βœ… EIGHT_SLEEP_EMAIL configured"
    else
        echo "❌ EIGHT_SLEEP_EMAIL missing"
    fi
    
    if grep -q "EIGHT_SLEEP_PASSWORD=" .env; then
        echo "βœ… EIGHT_SLEEP_PASSWORD configured"
    else
        echo "❌ EIGHT_SLEEP_PASSWORD missing"
    fi
else
    echo "❌ .env file missing (copy from .env.template)"
fi

# Check network connectivity
echo -e "\n🌐 Network Connectivity:"
if ping -c 1 auth-api.8slp.net &> /dev/null; then
    echo "βœ… Can reach auth-api.8slp.net"
else
    echo "❌ Cannot reach auth-api.8slp.net"
fi

if ping -c 1 client-api.8slp.net &> /dev/null; then
    echo "βœ… Can reach client-api.8slp.net"
else
    echo "❌ Cannot reach client-api.8slp.net"
fi

# Check mitmproxy installation (if needed)
echo -e "\nπŸ” mitmproxy (for traffic capture):"
if command -v mitmproxy &> /dev/null; then
    echo "βœ… mitmproxy installed: $(mitmproxy --version | head -n1)"
else
    echo "⚠️  mitmproxy not installed (needed for traffic capture only)"
fi

echo -e "\nπŸ”¬ Quick API Test:"
echo "Run: bun run src/test-eight-sleep-api.ts"
echo "This will test authentication and basic API connectivity"

echo -e "\nπŸ“‹ Next Steps:"
echo "1. Fix any ❌ issues shown above"
echo "2. Run the API test to verify authentication"
echo "3. Check specific sections below for detailed troubleshooting"

Quick API Test

# Test basic API connectivity
bun run src/test-eight-sleep-api.ts

# Test with debug output
DEBUG=1 bun run src/test-eight-sleep-api.ts

# Test individual components
bun run src/extract-credentials.ts

Authentication Issues

Invalid Credentials

Symptoms:

  • "Authentication failed" errors
  • 401 Unauthorized responses
  • "Invalid email or password" messages

Solutions:

  1. Verify Credentials
# Test login manually
curl -X POST https://auth-api.8slp.net/v1/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "your-email@example.com",
    "password": "your-password"
  }'
  1. Reset Password
# If you're unsure about your password
# 1. Go to Eight Sleep app or website
# 2. Use "Forgot Password" to reset
# 3. Update .env file with new password
  1. Check for Special Characters
# Ensure password is properly escaped in .env
# Wrap in quotes if it contains special characters
EIGHT_SLEEP_PASSWORD="password!with#special$chars"
  1. Try Alternative OAuth Credentials
# Use the known working OAuth credentials
EIGHT_SLEEP_CLIENT_ID=0894c7f33bb94800a03f1f4df13a4f38
EIGHT_SLEEP_CLIENT_SECRET=f0954a3e0e9b47348e98fc5f0b2d45c3b4ba1790e65973febc690037bdadceba

Token Expiration

Symptoms:

  • Works initially, then fails after some time
  • "Token expired" or similar messages

Solutions:

  1. Implement Auto-Refresh
// src/auth-manager.ts
class AuthManager {
  private token?: string;
  private tokenExpiry?: Date;
  
  async ensureValidToken() {
    if (!this.token || this.isTokenExpired()) {
      await this.refreshToken();
    }
    return this.token;
  }
  
  private isTokenExpired(): boolean {
    if (!this.tokenExpiry) return true;
    return new Date() >= this.tokenExpiry;
  }
  
  private async refreshToken() {
    console.log('πŸ”„ Refreshing authentication token...');
    const auth = await this.api.authenticate();
    this.token = auth.session?.token;
    
    // Set expiry to 1 hour from now (conservative)
    this.tokenExpiry = new Date(Date.now() + 3600000);
  }
}
  1. Add Retry Logic
async function apiCallWithRetry(apiCall: () => Promise<any>, maxRetries = 2) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await apiCall();
    } catch (error) {
      if (error.message.includes('401') && attempt < maxRetries) {
        console.log(`πŸ”„ Authentication failed, retrying... (${attempt}/${maxRetries})`);
        await api.authenticate();
        continue;
      }
      throw error;
    }
  }
}

OAuth Flow Issues

Symptoms:

  • Client credentials not working
  • Different behavior between auth endpoints

Solutions:

  1. Try Both Auth Endpoints
// The API automatically tries both, but you can test manually
async function testAuthEndpoints() {
  const payload = {
    email: process.env.EIGHT_SLEEP_EMAIL,
    password: process.env.EIGHT_SLEEP_PASSWORD
  };
  
  // Test primary endpoint
  try {
    const response1 = await fetch('https://auth-api.8slp.net/v1/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    console.log('Auth API result:', response1.status);
  } catch (error) {
    console.log('Auth API failed:', error.message);
  }
  
  // Test fallback endpoint
  try {
    const response2 = await fetch('https://client-api.8slp.net/v1/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    console.log('Client API result:', response2.status);
  } catch (error) {
    console.log('Client API failed:', error.message);
  }
}
  1. Check API Response Format
// Add detailed logging to understand response structure
private async request(url: string, options: RequestInit = {}) {
  const response = await fetch(url, options);
  const data = await response.json();
  
  // Debug logging
  console.log('API Response:', {
    url,
    status: response.status,
    headers: Object.fromEntries(response.headers.entries()),
    data: JSON.stringify(data, null, 2)
  });
  
  return data;
}

API Connection Problems

Network Connectivity Issues

Symptoms:

  • Connection timeouts
  • "ENOTFOUND" or "ECONNREFUSED" errors
  • Intermittent failures

Diagnostics:

  1. Test Network Connectivity
# Test DNS resolution
nslookup auth-api.8slp.net
nslookup client-api.8slp.net

# Test basic connectivity
curl -I https://auth-api.8slp.net
curl -I https://client-api.8slp.net

# Test with verbose output
curl -v https://auth-api.8slp.net/v1/health 2>&1
  1. Check Firewall/Proxy Settings
# Check if you're behind a corporate firewall
echo $HTTP_PROXY
echo $HTTPS_PROXY

# Test without proxy
unset HTTP_PROXY HTTPS_PROXY
curl -I https://auth-api.8slp.net

Solutions:

  1. Add Request Timeouts
private async request(url: string, options: RequestInit = {}) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), 10000); // 10 second timeout
  
  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal
    });
    clearTimeout(timeoutId);
    return response;
  } catch (error) {
    clearTimeout(timeoutId);
    if (error.name === 'AbortError') {
      throw new Error('Request timeout - check network connectivity');
    }
    throw error;
  }
}
  1. Implement Retry with Exponential Backoff
async function retryRequest(requestFn: () => Promise<any>, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await requestFn();
    } catch (error) {
      if (attempt === maxRetries) throw error;
      
      const delay = Math.pow(2, attempt) * 1000; // 2s, 4s, 8s
      console.log(`Retry ${attempt}/${maxRetries} after ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

API Rate Limiting

Symptoms:

  • 429 "Too Many Requests" errors
  • Temporary API blocks
  • Slower response times

Solutions:

  1. Implement Rate Limiting
class RateLimiter {
  private requests: number[] = [];
  private maxRequests: number;
  private windowMs: number;
  
  constructor(maxRequests = 20, windowMs = 60000) { // 20 requests per minute
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
  }
  
  async waitIfNeeded() {
    const now = Date.now();
    
    // Remove old requests outside the window
    this.requests = this.requests.filter(time => now - time < this.windowMs);
    
    if (this.requests.length >= this.maxRequests) {
      const oldestRequest = Math.min(...this.requests);
      const waitTime = this.windowMs - (now - oldestRequest);
      
      console.log(`⏳ Rate limit reached, waiting ${waitTime}ms...`);
      await new Promise(resolve => setTimeout(resolve, waitTime));
    }
    
    this.requests.push(now);
  }
}
  1. Cache API Responses
class CachedAPI extends SmartBedAPI {
  private cache = new Map<string, { data: any; expiry: number }>();
  
  async getStatus() {
    const cacheKey = 'status';
    const cached = this.cache.get(cacheKey);
    
    if (cached && Date.now() < cached.expiry) {
      console.log('πŸ“‹ Using cached status');
      return cached.data;
    }
    
    const data = await super.getStatus();
    this.cache.set(cacheKey, {
      data,
      expiry: Date.now() + 30000 // Cache for 30 seconds
    });
    
    return data;
  }
}

Device Not Found

Symptoms:

  • "Device not found" errors
  • Empty device list
  • Cannot control temperature

Solutions:

  1. Debug Device Discovery
async function debugDevices() {
  try {
    const devices = await api.getDevices();
    console.log('πŸ“± Devices found:', JSON.stringify(devices, null, 2));
    
    if (devices.length === 0) {
      console.log('❌ No devices found. Possible causes:');
      console.log('  - Account has no registered devices');
      console.log('  - Device is offline');
      console.log('  - Account permissions issue');
    }
    
    for (const device of devices) {
      console.log(`πŸ›οΈ Device: ${device.id || device.deviceId}`);
      console.log(`   Model: ${device.model || 'unknown'}`);
      console.log(`   Online: ${device.online ?? 'unknown'}`);
    }
  } catch (error) {
    console.error('Device discovery failed:', error);
  }
}
  1. Try Alternative Device APIs
// Some accounts might use different device endpoints
async function findDevicesAlternative() {
  const endpoints = [
    '/v1/users/{userId}/devices',
    '/v1/devices',
    '/v2/devices',
    '/v1/me/devices'
  ];
  
  for (const endpoint of endpoints) {
    try {
      const url = `${SmartBedAPI.CLIENT_API}${endpoint.replace('{userId}', this.userId)}`;
      const devices = await this.request(url);
      
      if (devices && devices.length > 0) {
        console.log(`βœ… Found devices at: ${endpoint}`);
        return devices;
      }
    } catch (error) {
      console.log(`❌ No devices at: ${endpoint}`);
    }
  }
  
  throw new Error('No devices found at any endpoint');
}

Certificate Pinning Issues

Traffic Interception Blocked

Symptoms:

  • mitmproxy shows no Eight Sleep traffic
  • SSL/TLS errors in proxy logs
  • App still works but no captured data

Solutions:

  1. Verify Certificate Installation
# Check if mitmproxy certificate is trusted
openssl s_client -connect auth-api.8slp.net:443 -verify_return_error

# On iOS: Settings > General > About > Certificate Trust Settings
# Ensure mitmproxy certificate is enabled for SSL

# On Android: Security > Trusted Credentials > User
# Verify mitmproxy certificate is installed
  1. Use Bypass Scripts
# Use the included bypass script
mitmproxy -s scripts/bypass-cert-pinning.py --ssl-insecure

# Or try different SSL modes
mitmproxy --ssl-insecure
mitmproxy --ignore-hosts ".*google.*"
  1. Alternative Capture Methods
# Method 1: Use Charles Proxy instead of mitmproxy
# Charles often has better certificate handling

# Method 2: Use a rooted Android device
# Install SSL Kill Switch or TrustMeAlready

# Method 3: Try Frida bypass (advanced)
frida -U -f com.eightsleep.eight -l ssl-bypass.js --no-pause

Certificate Trust Issues

Symptoms:

  • Certificate warnings in browser
  • "Certificate not trusted" errors
  • HTTPS interception failures

Solutions:

  1. Regenerate mitmproxy Certificates
# Remove old certificates
rm -rf ~/.mitmproxy

# Start mitmproxy to generate new certificates
mitmproxy

# Re-install on device
# Navigate to mitm.it on the device
  1. Check Certificate Details
# View certificate information
openssl x509 -in ~/.mitmproxy/mitmproxy-ca-cert.pem -text -noout

# Verify certificate fingerprint matches device
  1. Manual Certificate Installation
# Export certificate in different formats
cp ~/.mitmproxy/mitmproxy-ca-cert.pem cert.pem        # PEM format
openssl x509 -outform DER -in cert.pem -out cert.der  # DER format

Traffic Capture Problems

No Traffic Visible

Symptoms:

  • mitmproxy shows no requests
  • Empty capture files
  • Proxy appears to be working but no data

Diagnostics:

  1. Verify Proxy Settings
# Check device proxy configuration
# iOS: Settings > Wi-Fi > (i) > HTTP Proxy
# Android: Wi-Fi Settings > Advanced > Proxy

# Test proxy is working
curl -x localhost:8080 http://httpbin.org/ip
  1. Check Network Interface
# Find your IP address
ifconfig | grep "inet " | grep -v 127.0.0.1

# Test from device
curl -x YOUR_IP:8080 http://httpbin.org/ip

Solutions:

  1. Use Verbose Logging
# Run mitmproxy with verbose output
mitmproxy -v --set confdir=~/.mitmproxy

# Or use mitmdump for command-line output
mitmdump -s scripts/bypass-cert-pinning.py --ssl-insecure
  1. Test with Simple HTTP
# First test with non-HTTPS site
curl -x localhost:8080 http://httpbin.org/get

# Then test HTTPS
curl -x localhost:8080 -k https://httpbin.org/get
  1. Alternative Capture Script
# scripts/debug-capture.py
from mitmproxy import http

def request(flow: http.HTTPFlow) -> None:
    print(f"πŸ” Request: {flow.request.method} {flow.request.pretty_url}")
    
    if "8slp.net" in flow.request.pretty_url:
        print(f"βœ… Eight Sleep traffic detected!")
        print(f"   Headers: {dict(flow.request.headers)}")
        print(f"   Body: {flow.request.content.decode('utf-8', errors='ignore')}")

def response(flow: http.HTTPFlow) -> None:
    if "8slp.net" in flow.request.pretty_url:
        print(f"πŸ“₯ Response: {flow.response.status_code}")
        print(f"   Body: {flow.response.content.decode('utf-8', errors='ignore')}")

Capture Data Corruption

Symptoms:

  • Garbled or incomplete data
  • JSON parsing errors
  • Missing request/response parts

Solutions:

  1. Improve Capture Script
# scripts/robust-capture.py
import json
from mitmproxy import http

def response(flow: http.HTTPFlow) -> None:
    if "8slp.net" not in flow.request.pretty_url:
        return
    
    try:
        # Parse and pretty-print JSON
        if "application/json" in flow.response.headers.get("content-type", ""):
            data = json.loads(flow.response.content.decode('utf-8'))
            pretty_json = json.dumps(data, indent=2)
            
            # Save to file
            filename = f"captures/response_{flow.request.path.replace('/', '_')}.json"
            with open(filename, 'w') as f:
                f.write(pretty_json)
            
            print(f"πŸ’Ύ Saved response to {filename}")
    except Exception as e:
        print(f"❌ Failed to process response: {e}")
        # Save raw content anyway
        filename = f"captures/raw_response_{flow.id}.txt"
        with open(filename, 'wb') as f:
            f.write(flow.response.content)
  1. Increase Buffer Sizes
# Run mitmproxy with larger buffers
mitmproxy --set stream_large_bodies=1m --set confdir=~/.mitmproxy

Hardware Integration Issues

GPIO Permission Errors

Symptoms:

  • "Permission denied" accessing GPIO
  • "gpio" group errors
  • Pins not responding

Solutions:

  1. Fix Permissions
# Add user to gpio group
sudo usermod -a -G gpio $USER

# Logout and login again, then verify
groups | grep gpio

# Alternative: Change GPIO permissions
sudo chmod 666 /dev/gpiomem
  1. Enable GPIO in Boot Config
# Edit boot configuration
sudo nano /boot/config.txt

# Add or ensure these lines exist:
dtparam=gpio=on
enable_uart=1

# Reboot
sudo reboot
  1. Test GPIO Access
# Test GPIO access manually
echo "18" | sudo tee /sys/class/gpio/export
echo "in" | sudo tee /sys/class/gpio/gpio18/direction
cat /sys/class/gpio/gpio18/value

# Clean up
echo "18" | sudo tee /sys/class/gpio/unexport

Button Bouncing Issues

Symptoms:

  • Multiple button press events
  • Inconsistent button behavior
  • Rapid-fire activations

Solutions:

  1. Software Debouncing
class DebouncedButton {
  private lastPress = 0;
  private debounceTime: number;
  
  constructor(debounceTime = 300) {
    this.debounceTime = debounceTime;
  }
  
  isValidPress(): boolean {
    const now = Date.now();
    if (now - this.lastPress < this.debounceTime) {
      return false; // Too soon, ignore
    }
    this.lastPress = now;
    return true;
  }
}
  1. Hardware Debouncing
Add a capacitor between button pin and ground:
- 100nF ceramic capacitor
- Connect between GPIO pin and GND
- Helps filter electrical noise
  1. Interrupt-Based Handling
// Use interrupt-based GPIO instead of polling
await this.gpio.setup(pin, this.gpio.DIR_IN, this.gpio.EDGE_FALLING);

// Only trigger on falling edge (button press)
this.gpio.on('change', (channel: number, value: boolean) => {
  if (channel === pin && !value) { // Falling edge only
    this.handleButtonPress();
  }
});

Serial Communication Failures

Symptoms:

  • Serial port not found
  • Data corruption
  • Connection timeouts

Solutions:

  1. Fix Serial Permissions
# Add user to dialout group
sudo usermod -a -G dialout $USER

# Check available serial ports
ls -la /dev/tty* | grep -E "(USB|ACM)"

# Test serial connection
screen /dev/ttyUSB0 9600
  1. Robust Serial Handling
class RobustSerial {
  private port: SerialPort;
  private reconnectDelay = 5000;
  
  constructor(portPath: string) {
    this.port = new SerialPort({
      path: portPath,
      baudRate: 9600,
      autoOpen: false
    });
    
    this.setupErrorHandling();
  }
  
  private setupErrorHandling() {
    this.port.on('error', (error) => {
      console.error(`❌ Serial error: ${error.message}`);
      this.scheduleReconnect();
    });
    
    this.port.on('close', () => {
      console.log('πŸ”Œ Serial port closed');
      this.scheduleReconnect();
    });
  }
  
  private scheduleReconnect() {
    setTimeout(() => {
      console.log('πŸ”„ Attempting to reconnect...');
      this.port.open();
    }, this.reconnectDelay);
  }
}

Network and Connectivity

DNS Resolution Issues

Symptoms:

  • "ENOTFOUND" errors
  • Cannot resolve Eight Sleep domains
  • Intermittent connectivity

Solutions:

  1. Test DNS Resolution
# Test DNS servers
nslookup auth-api.8slp.net 8.8.8.8
nslookup auth-api.8slp.net 1.1.1.1

# Check system DNS
cat /etc/resolv.conf

# Flush DNS cache (macOS)
sudo dscacheutil -flushcache

# Flush DNS cache (Linux)
sudo systemctl restart systemd-resolved
  1. Use Alternative DNS
# Temporarily use Google DNS
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf

# Or add to network configuration

Firewall and Proxy Issues

Symptoms:

  • Connections blocked
  • Corporate firewall interference
  • Proxy authentication required

Solutions:

  1. Check Firewall Rules
# Check if ports are blocked
telnet auth-api.8slp.net 443
telnet client-api.8slp.net 443

# Test with different ports
curl -v --connect-timeout 10 https://auth-api.8slp.net
  1. Configure Proxy Authentication
// Add proxy support to API client
private async request(url: string, options: RequestInit = {}) {
  const proxyUrl = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;
  
  if (proxyUrl) {
    // Configure proxy authentication if needed
    const proxyAuth = process.env.PROXY_AUTH; // "username:password"
    if (proxyAuth) {
      options.headers = {
        ...options.headers,
        'Proxy-Authorization': `Basic ${Buffer.from(proxyAuth).toString('base64')}`
      };
    }
  }
  
  return fetch(url, options);
}

Performance Issues

Slow API Responses

Symptoms:

  • Long delays for temperature changes
  • Timeouts during authentication
  • Sluggish status updates

Solutions:

  1. Implement Connection Pooling
class OptimizedAPI extends SmartBedAPI {
  private keepAliveAgent: any;
  
  constructor(config: EightSleepConfig) {
    super(config);
    
    // Use HTTP keep-alive for better performance
    const https = require('https');
    this.keepAliveAgent = new https.Agent({
      keepAlive: true,
      keepAliveMsecs: 30000,
      maxSockets: 5
    });
  }
  
  private async request(url: string, options: RequestInit = {}) {
    return fetch(url, {
      ...options,
      agent: this.keepAliveAgent
    });
  }
}
  1. Add Performance Monitoring
class MonitoredAPI extends SmartBedAPI {
  private async request(url: string, options: RequestInit = {}) {
    const startTime = Date.now();
    
    try {
      const response = await super.request(url, options);
      const duration = Date.now() - startTime;
      
      console.log(`⏱️ API call took ${duration}ms: ${new URL(url).pathname}`);
      
      if (duration > 5000) {
        console.warn(`⚠️  Slow API response: ${duration}ms`);
      }
      
      return response;
    } catch (error) {
      const duration = Date.now() - startTime;
      console.error(`❌ API call failed after ${duration}ms:`, error.message);
      throw error;
    }
  }
}

Memory Leaks

Symptoms:

  • Increasing memory usage over time
  • Application becomes unresponsive
  • System performance degradation

Solutions:

  1. Monitor Memory Usage
// Add memory monitoring
setInterval(() => {
  const usage = process.memoryUsage();
  console.log('πŸ“Š Memory usage:', {
    rss: Math.round(usage.rss / 1024 / 1024) + 'MB',
    heapUsed: Math.round(usage.heapUsed / 1024 / 1024) + 'MB',
    external: Math.round(usage.external / 1024 / 1024) + 'MB'
  });
  
  // Alert if memory usage is high
  if (usage.rss > 500 * 1024 * 1024) { // 500MB
    console.warn('⚠️  High memory usage detected');
  }
}, 60000); // Check every minute
  1. Cleanup Resources
class ResourceManager {
  private timers: NodeJS.Timeout[] = [];
  private intervals: NodeJS.Timeout[] = [];
  
  setTimeout(callback: () => void, delay: number): NodeJS.Timeout {
    const timer = setTimeout(callback, delay);
    this.timers.push(timer);
    return timer;
  }
  
  setInterval(callback: () => void, delay: number): NodeJS.Timeout {
    const interval = setInterval(callback, delay);
    this.intervals.push(interval);
    return interval;
  }
  
  cleanup() {
    this.timers.forEach(clearTimeout);
    this.intervals.forEach(clearInterval);
    this.timers = [];
    this.intervals = [];
  }
}

// Use in main application
process.on('SIGINT', () => {
  resourceManager.cleanup();
  process.exit(0);
});

Error Messages Reference

Common Error Codes

Error Message Cause Solution
ENOTFOUND auth-api.8slp.net DNS resolution failure Check network connectivity, try different DNS
401 Unauthorized Invalid credentials or expired token Verify credentials, re-authenticate
429 Too Many Requests API rate limiting Implement request throttling
ECONNREFUSED Connection refused Check firewall, verify service is running
Certificate pinning failure SSL/TLS interception blocked Use bypass scripts or alternative methods
Permission denied (GPIO) Insufficient permissions Add user to gpio group
Device not found No devices associated with account Check device registration, try alternative endpoints
Timeout Network or API timeout Increase timeout values, check connectivity

Detailed Error Analysis

// Enhanced error handling with detailed diagnostics
class DiagnosticError extends Error {
  constructor(
    message: string,
    public code: string,
    public context: any = {},
    public suggestions: string[] = []
  ) {
    super(message);
    this.name = 'DiagnosticError';
  }
  
  static analyze(error: any): DiagnosticError {
    if (error.code === 'ENOTFOUND') {
      return new DiagnosticError(
        'DNS resolution failed',
        'DNS_ERROR',
        { hostname: error.hostname },
        [
          'Check internet connectivity',
          'Try different DNS servers (8.8.8.8, 1.1.1.1)',
          'Verify hostname spelling',
          'Check if behind corporate firewall'
        ]
      );
    }
    
    if (error.message.includes('401')) {
      return new DiagnosticError(
        'Authentication failed',
        'AUTH_ERROR',
        { statusCode: 401 },
        [
          'Verify email and password in .env file',
          'Try resetting your Eight Sleep password',
          'Check if account is locked or suspended',
          'Try alternative OAuth credentials'
        ]
      );
    }
    
    // Add more error analysis patterns...
    
    return new DiagnosticError(
      error.message,
      'UNKNOWN_ERROR',
      { originalError: error },
      ['Check logs for more details', 'Try restarting the application']
    );
  }
}

Debug Tools and Commands

API Debug Utilities

# Create debug script
cat > debug-api.sh << 'EOF'
#!/bin/bash

echo "πŸ” Eight Sleep API Debug Tool"
echo "============================"

# Test DNS resolution
echo "πŸ“‘ Testing DNS resolution..."
nslookup auth-api.8slp.net
nslookup client-api.8slp.net

# Test basic connectivity
echo -e "\n🌐 Testing connectivity..."
curl -w "\nTime: %{time_total}s\nStatus: %{http_code}\n" \
     -s -I https://auth-api.8slp.net

# Test authentication
echo -e "\nπŸ” Testing authentication..."
if [ -f ".env" ]; then
    source .env
    curl -X POST https://auth-api.8slp.net/v1/login \
         -H "Content-Type: application/json" \
         -w "\nStatus: %{http_code}\nTime: %{time_total}s\n" \
         -d "{\"email\":\"$EIGHT_SLEEP_EMAIL\",\"password\":\"$EIGHT_SLEEP_PASSWORD\"}"
else
    echo "❌ No .env file found"
fi

echo -e "\nβœ… Debug complete"
EOF

chmod +x debug-api.sh
./debug-api.sh

Application Debug Mode

// src/debug-mode.ts
class DebugManager {
  private static instance: DebugManager;
  private debugLevel: number;
  
  constructor() {
    this.debugLevel = parseInt(process.env.DEBUG_LEVEL || '0');
    
    if (process.env.DEBUG) {
      this.debugLevel = Math.max(this.debugLevel, 1);
    }
  }
  
  static getInstance(): DebugManager {
    if (!this.instance) {
      this.instance = new DebugManager();
    }
    return this.instance;
  }
  
  log(level: number, message: string, data?: any) {
    if (level <= this.debugLevel) {
      const timestamp = new Date().toISOString();
      const prefix = ['', 'πŸ”', 'πŸ“Š', 'πŸ”§'][level] || 'πŸ“';
      
      console.log(`${prefix} [${timestamp}] ${message}`);
      
      if (data) {
        console.log(JSON.stringify(data, null, 2));
      }
    }
  }
  
  debug(message: string, data?: any) { this.log(1, message, data); }
  info(message: string, data?: any) { this.log(2, message, data); }
  trace(message: string, data?: any) { this.log(3, message, data); }
}

// Usage throughout application
const debug = DebugManager.getInstance();
debug.debug('API request starting', { url, method });

Network Diagnostics

# Network diagnostic script
cat > network-diag.sh << 'EOF'
#!/bin/bash

echo "🌐 Network Diagnostics for Eight Sleep Controller"
echo "================================================"

# Check network interfaces
echo "πŸ“‘ Network Interfaces:"
ifconfig | grep -A 1 "flags=.*UP"

# Check routing
echo -e "\nπŸ—ΊοΈ  Default Route:"
route -n | head -3

# Test external connectivity
echo -e "\n🌍 External Connectivity:"
ping -c 3 8.8.8.8 | grep "packet loss"

# Test Eight Sleep domains
echo -e "\nπŸ›οΈ  Eight Sleep Connectivity:"
for domain in auth-api.8slp.net client-api.8slp.net app-api.8slp.net; do
    echo -n "  $domain: "
    if ping -c 1 -W 3 "$domain" &>/dev/null; then
        echo "βœ… Reachable"
    else
        echo "❌ Unreachable"
    fi
done

# Check open ports
echo -e "\nπŸ”Œ Port Tests:"
for port in 80 443 8080; do
    echo -n "  Port $port: "
    if nc -z -w3 auth-api.8slp.net "$port" 2>/dev/null; then
        echo "βœ… Open"
    else
        echo "❌ Closed/Filtered"
    fi
done

echo -e "\nβœ… Network diagnostics complete"
EOF

chmod +x network-diag.sh
./network-diag.sh

Advanced Troubleshooting

Packet-Level Analysis

# Capture network traffic for analysis
sudo tcpdump -i any -w eight-sleep-traffic.pcap \
  host auth-api.8slp.net or host client-api.8slp.net

# Analyze with Wireshark
wireshark eight-sleep-traffic.pcap

# Command-line analysis
tcpdump -r eight-sleep-traffic.pcap -A | grep -i "eight\|sleep\|auth"

System Resource Monitoring

# Monitor application resources
cat > monitor-resources.sh << 'EOF'
#!/bin/bash

PID=$(pgrep -f "eight-sleep\|smartbed")

if [ -z "$PID" ]; then
    echo "❌ Application not running"
    exit 1
fi

echo "πŸ“Š Monitoring Eight Sleep Controller (PID: $PID)"
echo "Press Ctrl+C to stop"

while true; do
    # CPU and memory usage
    ps -p "$PID" -o pid,pcpu,pmem,rss,vsz,time,cmd 2>/dev/null || break
    
    # Network connections
    echo "🌐 Network connections:"
    lsof -p "$PID" -i 2>/dev/null | grep -E "(auth-api|client-api)" || echo "  None active"
    
    # Open files
    echo "πŸ“ Open files: $(lsof -p "$PID" 2>/dev/null | wc -l)"
    
    echo "----------------------------------------"
    sleep 5
done
EOF

chmod +x monitor-resources.sh
./monitor-resources.sh

Application State Debugging

// src/state-debugger.ts
class StateDebugger {
  private states: Map<string, any> = new Map();
  
  saveState(key: string, data: any) {
    this.states.set(key, {
      data: JSON.parse(JSON.stringify(data)), // Deep copy
      timestamp: new Date().toISOString()
    });
  }
  
  dumpState() {
    console.log('πŸ—‚οΈ  Application State Dump:');
    console.log('============================');
    
    for (const [key, state] of this.states.entries()) {
      console.log(`πŸ“‹ ${key}:`);
      console.log(`   Time: ${state.timestamp}`);
      console.log(`   Data: ${JSON.stringify(state.data, null, 2)}`);
      console.log('');
    }
  }
  
  compareStates(key: string, previous: any) {
    const current = this.states.get(key);
    if (!current) return;
    
    console.log(`πŸ”„ State changes for ${key}:`);
    
    // Simple diff implementation
    const currentData = JSON.stringify(current.data, null, 2);
    const previousData = JSON.stringify(previous, null, 2);
    
    if (currentData !== previousData) {
      console.log('   Previous:', previousData);
      console.log('   Current:', currentData);
    } else {
      console.log('   No changes detected');
    }
  }
}

// Usage in main application
const stateDebugger = new StateDebugger();

// Save states at key points
stateDebugger.saveState('authentication', { token: '***', userId: api.userId });
stateDebugger.saveState('device-status', await api.getStatus());

// Dump state on error
process.on('uncaughtException', (error) => {
  console.error('πŸ’₯ Uncaught exception:', error);
  stateDebugger.dumpState();
  process.exit(1);
});

This troubleshooting guide provides comprehensive coverage of common issues and their solutions. Use the quick diagnostics section first, then dive into specific problem areas as needed. The debug tools and monitoring scripts will help identify and resolve issues quickly.