> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uplink.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Device Management

> Managing mobile devices in your Uplink automation workflows

Effective device management is crucial for scaling your mobile automation. Whether you're testing on a single device or orchestrating dozens simultaneously, understanding device management patterns will help you build robust automation workflows.

## Understanding devices

In Uplink, a **device** (also called a **worker**) is a mobile phone or tablet that connects to your session and can host browsers. Devices can join sessions through either:

* **Connect app**: Users scan a QR code or enter a session code
* **SDK integration**: Your custom app connects programmatically

## Connecting devices

### Waiting for devices

The most common pattern is to wait for a device to connect before starting automation:

```typescript theme={null}
const session = await uplink.session('<project-api-key>', {
  include: { ecdsa: true, ecdh: true }
})
const client = await uplink.client.fromSession(session)

// Check for connected workers
const workers = client.workers()
if (workers.length > 0) {
  const worker = workers[0]
  console.log('Worker connected:', worker.address)

  // Now you can use the worker
  const browser = await worker.launch()
} else {
  console.log('No workers connected yet, waiting...')
  // Use worker-connected event to handle when devices join
}
```

### Listing connected devices

Get all currently connected devices:

```typescript theme={null}
const devices = client.workers()

console.log(`${devices.length} devices connected`)
devices.forEach(device => {
  console.log('Device:', device.address)
})
```

### Device connection events

React to devices connecting and disconnecting in real-time:

```typescript theme={null}
client.on('worker-connected', async (device) => {
  console.log('New device connected:', device.address)

  // Automatically start automation on new devices
  const browser = await device.launch()
  const page = await browser.newPage()
  await page.goto('https://example.com')
})

client.on('worker-disconnected', (device) => {
  console.log('Device disconnected:', device.address)
})
```

## Device information

Retrieve information about a device's capabilities and specifications:

```typescript theme={null}
const info = await device.getDeviceInfo()

console.log('Device model:', info.deviceModel)      // e.g., "iPhone 14 Pro"
console.log('Platform:', info.platform)             // "iOS" or "Android"
console.log('OS version:', info.platformVersion)    // e.g., "17.2"
console.log('Type:', info.deviceType)              // "phone" or "tablet"
```

### Using device info for targeting

```typescript theme={null}
const devices = client.workers()

// Find iOS devices
const iosDevices = []
for (const device of devices) {
  const info = await device.getDeviceInfo()
  if (info.platform === 'iOS') {
    iosDevices.push(device)
  }
}

// Test on a specific device type
const iphone = devices.find(async d => {
  const info = await d.getDeviceInfo()
  return info.deviceModel.includes('iPhone')
})
```

## Multi-device patterns

### Parallel execution

Run automation on multiple devices simultaneously:

```typescript theme={null}
const session = await uplink.session('<project-api-key>', {
  include: { ecdsa: true, ecdh: true }
})
const client = await uplink.client.fromSession(session)

// Get connected workers (ensure 3+ devices are connected)
const workers = client.workers()
const worker1 = workers[0]
const worker2 = workers[1]
const worker3 = workers[2]

// Run tests in parallel
await Promise.all([
  runTest(worker1),
  runTest(worker2),
  runTest(worker3)
])

async function runTest(device) {
  const browser = await device.launch()
  const page = await browser.newPage()
  await page.goto('https://example.com')
  // ... test logic
  await browser.close()
}
```

### Device pool management

Manage a pool of devices for load distribution:

```typescript theme={null}
class DevicePool {
  constructor(client) {
    this.client = client
    this.available = []
    this.busy = new Set()

    client.on('worker-connected', (device) => {
      this.available.push(device)
    })
  }

  async acquire() {
    // Wait for an available device
    while (this.available.length === 0) {
      await new Promise(resolve => setTimeout(resolve, 100))
    }

    const device = this.available.shift()
    this.busy.add(device)
    return device
  }

  release(device) {
    this.busy.delete(device)
    this.available.push(device)
  }
}

// Usage
const pool = new DevicePool(client)

async function runAutomation() {
  const device = await pool.acquire()
  try {
    const browser = await device.launch()
    // ... automation logic
    await browser.close()
  } finally {
    pool.release(device)
  }
}

// Run multiple automations
await Promise.all([
  runAutomation(),
  runAutomation(),
  runAutomation()
])
```

### Platform-specific testing

Run different tests based on platform:

```typescript theme={null}
client.on('worker-connected', async (device) => {
  const info = await device.getDeviceInfo()

  if (info.platform === 'iOS') {
    await runIOSTest(device)
  } else if (info.platform === 'Android') {
    await runAndroidTest(device)
  }
})

async function runIOSTest(device) {
  const browser = await device.launch()
  // iOS-specific test logic
  await browser.close()
}

async function runAndroidTest(device) {
  const browser = await device.launch()
  // Android-specific test logic
  await browser.close()
}
```

## Browser management per device

Each device can host multiple browsers. Manage them effectively:

### Launching browsers

```typescript theme={null}
// Launch a new browser on a specific device
const browser = await device.launch()

// Or let the client pick a device
const browser = await client.launch()
```

### Listing browsers

```typescript theme={null}
// Get all browsers on a device
const browsers = await device.browsers()

console.log(`${browsers.length} browsers running on device`)
```

### Connecting to existing browsers

Browsers are persistent and can be reconnected using their handle:

```typescript theme={null}
// Save the browser handle
const browser = await device.launch()
const handle = browser.handle

// Later, reconnect to the same browser
const sameBrowser = await device.connect(handle)

// Or from the client level
const sameBrowser = await client.connect(handle)
```

### Cleaning up browsers

Always close browsers when done to free device resources:

```typescript theme={null}
// Close all browsers on a device
const browsers = await device.browsers()
await Promise.all(browsers.map(b => b.close()))
```

## Device lifecycle

### Terminating devices

When necessary, you can terminate a device connection:

```typescript theme={null}
// Terminate a specific device
await device.terminate()

// Or from the client level
await client.terminate(device.address)
```

<Warning>
  Terminating a device closes all browsers and disconnects the device from the session. Only do this when you're certain you're done with the device.
</Warning>

### Handling disconnections

Devices can disconnect unexpectedly (network issues, app closure, etc.). Handle disconnections gracefully:

```typescript theme={null}
const activeDevices = new Map()

client.on('worker-connected', (device) => {
  activeDevices.set(device.address, device)
})

client.on('worker-disconnected', (device) => {
  console.warn('Device disconnected:', device.address)
  activeDevices.delete(device.address)

  // Implement reconnection logic or failover
  handleDeviceFailure(device.address)
})

async function handleDeviceFailure(address) {
  // Retry logic, notification, or reassignment
}
```

## Addressing devices

Devices have unique addresses for identification and targeting:

```typescript theme={null}
const devices = client.workers()

devices.forEach(device => {
  console.log('Address:', device.address)
})

// Use a specific device by address
const targetDevice = devices.find(d => d.address === 'device-123')
if (targetDevice) {
  const browser = await targetDevice.launch()
}
```

## Best practices

<AccordionGroup>
  <Accordion title="Clean up resources" icon="broom">
    Always close browsers and pages when done to free device resources:

    ```typescript theme={null}
    try {
      const browser = await device.launch()
      const page = await browser.newPage()
      // ... automation
    } finally {
      await page.close()
      await browser.close()
    }
    ```
  </Accordion>

  <Accordion title="Handle device variability" icon="mobile-screen-button">
    Different devices have different capabilities. Test your automation across various device types and OS versions.

    ```typescript theme={null}
    const info = await device.getDeviceInfo()
    if (parseFloat(info.platformVersion) < 15) {
      console.warn('Old OS version, some features may not work')
    }
    ```
  </Accordion>

  <Accordion title="Monitor device health" icon="heart-pulse">
    Keep track of device connection stability and browser success rates:

    ```typescript theme={null}
    const stats = {
      connected: 0,
      disconnected: 0,
      failures: 0
    }

    client.on('worker-connected', () => stats.connected++)
    client.on('worker-disconnected', () => stats.disconnected++)
    ```
  </Accordion>

  <Accordion title="Use timeouts" icon="timer">
    Set appropriate timeouts when waiting for devices or operations:

    ```typescript theme={null}
    // Wait for workers with timeout using event pattern
    const timeout = 30000 // 30 seconds

    const getWorkerWithTimeout = () => {
      return new Promise((resolve, reject) => {
        const timer = setTimeout(() => {
          reject(new Error('Device connection timeout'))
        }, timeout)

        const checkWorkers = () => {
          const workers = client.workers()
          if (workers.length > 0) {
            clearTimeout(timer)
            resolve(workers[0])
          }
        }

        client.on('worker-connected', checkWorkers)
        checkWorkers() // Check immediately
      })
    }

    const worker = await getWorkerWithTimeout()
    ```
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Client API" icon="plug" href="/api-reference/client">
    Full Client API reference
  </Card>

  <Card title="ClientWorker API" icon="mobile" href="/api-reference/client-worker">
    Device-specific methods
  </Card>

  <Card title="Browser API" icon="browser" href="/api-reference/browser">
    Managing browsers
  </Card>

  <Card title="Core concepts" icon="book" href="/fundamentals/core-concepts">
    Review core architecture
  </Card>
</CardGroup>
