P2P Capabilities

Last Updated on : 2026-03-06 03:20:26download

Overview

The P2P capabilities of the robot vacuum are implemented based on the IP camera’s P2P and file transfer functionalities. In addition to the basic Tuya SDK, the following two libraries are required for direct use.

SDK:

  • @thingsmart/thingp2pfiletranssdk: P2P file transfer core library.
  • @thingsmart/ipcsdk: IPC functionality library.

Platform: HarmonyOS (ArkTS)

Import module

// IPC SDK manager
import { ThingIPCSdk } from '@thingsmart/ipcsdk'

// P2P file transfer core
import {
  ThingP2pFileTransManager,      // File transfer manager (static class)
  ThingP2pFileTransInterface,    // File transfer interface
  ThingP2pFileTransListener      // File transfer listener
} from '@thingsmart/thingp2pfiletranssdk'

ThingIPCSdk module

getP2P

Functional description

Gets a P2P management instance to manage the lifecycle of P2P connections. This is a static method in the singleton pattern.

Function signature

static getP2P(): IThingP2P

Return value

Type Description
IThingP2P The P2P management instance, including methods for initialization, connection, and disconnection.
  • This is a static method, called directly using the class name.
  • It returns a singleton object, and multiple calls return the same instance.

Example

const p2pManager = ThingIPCSdk.getP2P()

initP2P

Functional description

Initializes the P2P SDK. This is a necessary step before using any of the SDK’s functionalities. The initialization process configures the SDK runtime environment and registers essential services and components.

Function signature

initP2P(userId: string): number

Parameters

Parameter Type Required Description
userId string Yes The user ID used to identify the current user.

Return value

Type Description
number Return result: 0 indicates success, and other values ​​indicate failure.
  • This method must be called before any P2P operations.
  • It is recommended to perform the initialization as early as possible when the application starts.
  • This method is synchronous, and its return value indicates the initialization result.
  • Repeated initializations will be ignored, and it is advisable to use a flag to control this.

Example

const result = ThingIPCSdk.getP2P().initP2P('user_12345')
if (result === 0) {
  L.i('P2PDemo', 'SDK initialization was successful')
} else {
  L.e('P2PDemo', `SDK initialization failed: ${result}`)
}

connectWithDevId

Functional description

Connects to the specified device and establishes a P2P session. Upon successful connection, a sessionId (handle) is returned via the callback for subsequent file transfer operations.

Function signature

connectWithDevId(remoteId: string, lanMode: number, timeout: number, callback: IThingP2PCallback): string

Parameters

Parameter Type Required Description
remoteId string Yes The device ID (an identifier for a remote device).
lanMode number Yes The connection mode. Valid values:
  • 0: Internet mode
  • 1: Local Area Network (LAN) mode
timeout number Yes The timeout duration in milliseconds. Recommended value: no less than 10,000 ms.
callback IThingP2PCallback Yes The P2P connection callback.

IThingP2PCallback definition

interface IThingP2PCallback {
  onResult(handle: number): void
}
Callback Parameter Description
onResult handle: number The connection result callback. The handle value represents the sessionId:
  • handle > 0: Connection was successful. The value is the sessionId.
  • handle = 0: Initial state.
  • handle < 0: Connection failed or disconnected.

Return value

Type Description
string The trace ID (traceId) of the connection request.

Session handle values

Value Description
> 0 Connection was successful. The value is the sessionId, which must be set on the file transfer instance.
0 Initial state.
-1 Connection failed.
-2 The connection is closed.
-3 The connection timed out.
  • initP2P must be called before establishing a connection.
  • The handle parameter in the onResult callback (when the value is > 0) is the sessionId, which must be set on the file transfer instance.
  • Internet mode (0) is suitable for remote connections, whereas LAN mode (1) is for fast connections within the same network.
  • Adjust the timeout according to network conditions. It is recommended to be no less than 10 seconds.
  • Re-connecting to the same device will first close the previous connection.
  • onResult will be called multiple times to notify of session state changes.

Example

// Define callback
const callback: IThingP2PCallback = {
  onResult: (handle: number) => {
    L.i('P2PDemo', `P2P connection result: handle=${handle}`)

    if (handle > 0) {
      // Connection was successful. The handle is the sessionId, which needs to be set on the file transfer instance.
      p2pFileTransfer.session = handle
      L.i('P2PDemo', `Connection successful, sessionId set: ${handle}`)
    } else if (handle === 0) {
      L.i('P2PDemo', 'Connection initializing...')
    } else {
      // handle < 0 indicates connection failure or disconnection
      L.e('P2PDemo', `Connection failed or disconnected: ${handle}`)
    }
  }
}

// Initiate a connection
ThingIPCSdk.getP2P().connectWithDevId(
  'device_abc123',  // Device ID
  0,                // Internet mode
  15000,            // 15-second timeout
  callback          // Callback interface
)

Complete example (combined with file transfer)

class P2PManager {
  private p2pFileTransfer: ThingP2pFileTransInterface | null = null
  private sessionId: number = -1

  async connectDevice(deviceId: string) {
    // 1. Create a file transfer listener
    const listener: ThingP2pFileTransListener = {
      onSessionStatusChanged: (sessionId, sessionStatus) => {
        L.i('P2PDemo', `Session status: ${sessionStatus}`)
        return 0
      },
      // ... Other callbacks
    }

    // 2. Create a file transfer instance
    this.p2pFileTransfer = ThingP2pFileTransManager.createP2pFileTransfer(
      deviceId,
      listener
    )

    // 3. Define the connection callback
    const callback: IThingP2PCallback = {
      onResult: (handle: number) => {
        if (handle > 0) {
          // 🔥 Save sessionId and set it to the file transfer instance
          this.sessionId = handle
          if (this.p2pFileTransfer) {
            this.p2pFileTransfer.session = handle
            L.i('P2PDemo', `✅ sessionId set: ${handle}`)
          }
        } else if (handle < 0) {
          L.e('P2PDemo', `Connection failed: ${handle}`)
          this.sessionId = -1
        }
      }
    }

    // 4. Initiate a connection
   ThingIPCSdk.getP2P().connectWithDevId(
      deviceId,
      0,      // Internet mode
      15000,  // Timeout period
      callback
    )

  }
}

disConnect

Functional description

Disconnects the P2P connection with a specified device and releases associated resources.

Function signature

disConnect(handle: number, reason: number, forced: boolean): number

Parameters

Parameter Type Required Description
handle number Yes The session handle (sessionId), obtained from the connectWithDevId callback.
reason number Yes Disconnection reason code: 0 indicates normal disconnection. Other values indicate unexpected disconnection.
forced boolean Yes Specifies whether to forcibly disconnect. Valid values:
  • true: Disconnect immediately.
  • false: Disconnect gracefully.

Return value

Type Description
number Return result: 0 indicates success, and other values ​​indicate failure.
  • The corresponding file transfer instance should be destroyed before disconnecting.
  • The handle parameter must be a valid sessionId (> 0).
  • Forced disconnection releases resources immediately and might cause data loss.
  • Graceful disconnection waits for the current transfer task to complete.
  • It is recommended to disconnect promptly when the device is no longer needed.

Example

// Assume the sessionId obtained from the connection callback
const sessionId = 12345

// Method 1: Normal disconnection (graceful)
const result = ThingIPCSdk.getP2P().disConnect(
  sessionId,  // Session handle
  0,          // Normal disconnection
  false       // Graceful disconnection
)

if (result === 0) {
  L.i('P2PDemo', 'Device disconnected successfully')
} else {
  L.e('P2PDemo', `Device disconnection failed: ${result}`)
}

// Method 2: Forced disconnection
const forceResult = ThingIPCSdk.getP2P().disConnect(
  sessionId,  // Session handle
  1,          // Unexpected disconnection
  true        // Forced disconnection
)

deInitP2P

Functional description

Deinitializes the P2P SDK and releases all resources. This is typically called when the application exits.

Function signature

deInitP2P(): number

Return value

Type Description
number Return result: 0 indicates success, and other values ​​indicate failure.
  • All device connections must be closed before deinitialization.
  • After deinitialization, initP2P needs to be called again before the application can be used.
  • This method is typically called when the application exits or is not used for an extended period.
  • This method is synchronous, and its return value indicates the deinitialization result.

Example

// Disconnect all connections first
const disconnectResult = ThingIPCSdk.getP2P().disConnect(sessionId, 0, false)
if (disconnectResult !== 0) {
  L.e('P2PDemo', `Disconnection failed: ${disconnectResult}`)
}

// Deinitialize the SDK
const result = ThingIPCSdk.getP2P().deInitP2P()
if (result === 0) {
  L.i('P2PDemo', 'SDK deinitialization was successful')
} else {
  L.e('P2PDemo', `SDK deinitialization failed: ${result}`)
}

ThingP2pFileTransManager module

createP2pFileTransfer

Functional description

Creates a P2P file transfer instance. An independent file transfer instance must be created for each device, used for operations such as querying file indexes, uploading, and downloading files.

Function signature

static createP2pFileTransfer(
  deviceId: string,
  listener: ThingP2pFileTransListener
): ThingP2pFileTransInterface

Parameters

Parameter Type Required Description
deviceId string Yes The device ID. It must match the device ID used when connecting.
listener ThingP2pFileTransListener Yes The file transfer listener used to receive various callback events.

Return value

Type Description
ThingP2pFileTransInterface The file transfer instance. It provides methods for querying, uploading, and downloading.
  • An independent file transfer instance must be created for each device.
  • After creating the instance, you must set the sessionId obtained from the successful connection state callback to the session property of the instance.
  • The instance is bound to its listener, and listeners cannot be shared.
  • When destroying an instance, the destroyP2pFileTransfer() method must be called.

Example

// 1. Create a listener
const listener: ThingP2pFileTransListener = {
  onSessionStatusChanged: (sessionId, status) => {
    L.i('P2PDemo', `session status: ${status}`)
    return 0
  },
  onFileFinished: (event, filename, index, errCode) => {
    L.i('P2PDemo', `file completed: ${filename}`)
    return 0
  },
  // ... other callbacks
}

// 2. Create a file transfer instance
const p2pFileTransfer = ThingP2pFileTransManager.createP2pFileTransfer(
  'device_abc123',
  listener
)

// 3. Set the sessionId after connecting to the device (Important!)
ThingIPCSdk.getP2P().connectWithDevId(
  { deviceId: 'device_abc123' },
  (event) => {
    if (event.status > 0) {
      // Key step: Set sessionId
      p2pFileTransfer.session = event.status
      L.i('P2PDemo', `sessionId already set: ${event.status}`)
    }
  },
  () => L.i('P2PDemo', 'Connection was successful'),
  (code, msg) => L.e('P2PDemo', `Connection failed: ${msg}`)
)

getSDKVersion

Functional description

Gets the version number of the P2P file transfer SDK.

Function signature

static getSDKVersion(): string

Return value

Type Description
string The SDK version number, in the format "1.0.0".

It is used for debugging and compatibility checks.

Example

const version = ThingP2pFileTransManager.getSDKVersion()
L.i('P2PDemo', `SDK version: ${version}`)

ThingP2pFileTransInterface instance method

session (Property)

Functional description

The session ID property, which must be set after a successful connection. This is the core identifier for P2P file transfer.

Property type

session: number

Description

Property Type Description
session number The session ID, obtained from the status callback of connectWithDevId.
  • This is the most critical step. It must be set immediately after a successful connection.
  • The sessionId is derived from event.status in the onStatusChange callback of connectWithDevId (when status is > 0).
  • If this property is not set, subsequent query and transfer operations will fail.

Example

// Set in connection status callback
ThingIPCSdk.getP2P().connectWithDevId(
  { deviceId: 'device_abc123' },
  (event) => {
    if (event.status > 0) {
      // Set sessionId to file transfer instance
      p2pFileTransfer.session = event.status
      L.i('P2PDemo', `session already set: ${event.status}`)
    }
  },
  () => {},
  (code, msg) => {}
)

queryFileIndexs

Functional description

Queries the file index list for a specified album. The returned file index contains information such as file name, type, and time, which can be used for subsequent download operations.

Function signature

queryAlbumFile(
  albumName: string,
): number

Parameters

Parameter Type Required Description
albumName string Yes The album name, such as "Camera" and "Maps".

Return value

Type Description
number Return result: 0 indicates success, and other values ​​indicate failure.
  • The device must be connected and the sessionId must be set before querying.
  • The actual query result data is returned in the listener’s onP2pResponse callback (event = 12).
  • The callback parameter might not be invoked and is present only for compatibility.
  • The query result data format is P2PAlbumFileIndexs, containing count and items fields.

Example

// Method 1: Using a listener to receive data (Recommended)
const listener: ThingP2pFileTransListener = {
  onP2pResponse: (event, errCode, data) => {
    if (event === 12) {  // Query file index event
      if (errCode === 0 && data) {
        const result: P2PAlbumFileIndexs = JSON.parse(data)
        L.i('P2PDemo', `Query was successful: count=${result.count}, items=${result.items.length}`)

        // Process the file list
        result.items.forEach(file => {
          L.i('P2PDemo', `File: ${file.filename}, type: ${file.type}`)
        })
      }
    }
    return 0
  },
  // ... Other callbacks
}

// Call the query method
const result = p2pFileTransfer.queryFileIndexs('Camera', (indexes) => {
  // Note: This callback might not be invoked
  L.i('P2PDemo', `Query callback: ${indexes.length}`)
})

if (result !== 0) {
  L.e('P2PDemo', `Query call failed: ${result}`)
}

startDownloadFiles

Functional description

Starts downloading the specified list of files to a local directory. Supports bulk downloading, with download progress notified via listener callbacks.

Function signature

startDownloadFiles(
  albumName: string,
  savePath: string,
  fileIndexes: string
): number

Parameters

Parameter Type Required Description
albumName string Yes The album name, which must be the same as used in the query.
savePath string Yes The save path. It must be an absolute path and the directory must exist.
fileIndexes string Yes JSON string of the file index array, generated via JSON.stringify(FileIndex[]).

Return value

Type Description
number Return result: 0 indicates success, and other values ​​indicate failure.
  • The file index must be queried first before downloading.
  • The save path must be an existing directory, otherwise the download will fail.
  • It is recommended to use HarmonyOS fs.mkdirSync() to ensure the directory exists.
  • This method should be called in the listener’s onP2pResponse callback (after a successful query).
  • Download progress is notified via the onFileProgress callback.
  • Download completion is notified via the onFileFinished callback.

Example

// Receive query results and automatically download them in onP2pResponse
onP2pResponse: (event, errCode, data) => {
  if (event === 12 && errCode === 0 && data) {
    const result: P2PAlbumFileIndexs = JSON.parse(data)

    // Prepare the save path
    const savePath = '/data/storage/el2/base/haps/entry/files/downloads'
    if (!fs.accessSync(savePath)) {
      fs.mkdirSync(savePath, true)
    }

    // Call the download in the query success callback
    const fileIndexesJson = JSON.stringify(result.items)
    const downloadResult = p2pFileTransfer.startDownloadFiles(
      'ipc_sweeper_robot',
      savePath,
      fileIndexesJson
    )

    if (downloadResult === 0) {
      L.i('P2PDemo', `Started downloading ${result.items.length} files`)
    } else {
      L.e('P2PDemo', `Download failed: ${downloadResult}`)
    }
  }
  return 0
}

startDownloadStream

Functional description

Downloads files in a streaming manner, suitable for data that needs real-time processing, such as maps. Data is returned packet by packet through the listener’s onStreamRecved callback.

Function signature

startDownloadStream(
  albumName: string,
  fileIndexes: string
): number

Parameters

Parameter Type Required Description
albumName string Yes The album name.
fileIndexes string Yes The JSON string of the file index array.

Return value

Type Description
number Return result: 0 indicates success, and other values ​​indicate failure.
  • Streaming download does not save files to disk. Data is processed within the onStreamRecved callback.
  • This is suitable for real-time processing scenarios, such as map rendering.
  • Data packets are returned in order and need to be reassembled manually.

Example

const fileIndexesJson = JSON.stringify([
  { idx: 0, channel: 0, type: 2, filename: 'map.dat', /* ... */ }
])

const result = p2pFileTransfer.startDownloadStream('Maps', fileIndexesJson)
if (result === 0) {
  L.i('P2PDemo', 'Streaming download has started')
}

destroy

Functional description

Destroys the file transfer instance and releases related resources. This method should be called before disconnecting the device.

Function signature

destroy(): void
  • The file transfer instance must be destroyed before disconnecting the device.
  • After destroy, the instance cannot be reused and must be recreated.

Example

// Destroy the file transfer instance
p2pFileTransfer.destroy()

// Then disconnect the device
ThingIPCSdk.getP2P().disconnect({ deviceId: 'device_abc123' }, () => {}, () => {})

ThingP2pFileTransListener callbacks

onSessionStatusChanged

Functional description

A callback for session status changes. It is triggered when the device connection status changes, used to monitor the connection status.

Function signature

onSessionStatusChanged(sessionId: number, sessionStatus: number): number

Parameters

Parameter Type Description
sessionId number The session ID.
sessionStatus number The session status. Valid values:
  • > 0: Connection successful.  
  • < 0: Disconnected/Failed.

Return value

Type Description
number Returns 0 or the sessionId by default.

Status value description

Status value Description
> 0 Connection successful. The value is the sessionId.
0 Initial state.
< 0 Connection disconnected or failed.

Example

onSessionStatusChanged: (sessionId, sessionStatus) => {
  if (sessionStatus > 0) {
    L.i('P2PDemo', `Connected,sessionId: ${sessionStatus}`)
  } else if (sessionStatus < 0) {
    L.e('P2PDemo', 'Disconnected')
  }
  return 0
}

onFileFinished

Functional description

A callback for file transfer completion. It is triggered when a single file upload or download finishes.

Function signature

onFileFinished(
  event: number,
  filename: string,
  index: number,
  errCode: number
): number

Parameters

Parameter Type Description
event number The event type. Valid values:
  • 0: Download
  • 2: Upload
filename string The file name.
index number The file index.
errCode number The error code. 0 indicates success, and other values ​​indicate failure.

Return value

Type Description
number Returns 0 by default.

Example

onFileFinished: (event, filename, index, errCode) => {
  if (event === 0) {
    L.i('P2PDemo', `File download completed: ${filename}, Result: ${errCode === 0 ? 'Success' : 'Failure'}`)
  } else if (event === 2) {
    L.i('P2PDemo', `File upload completed: ${filename}`)
  }
  return 0
}

onProgress

Functional description

A Callback for overall transfer progress. It is used to display the overall transfer progress.

Function signature

onProgress(event: number, progress: number): number

Parameters

Parameter Type Description
event number The event type. Valid values:
  • 0: Download
  • 2: Upload
progress number The progress percentage, ranging from 0 to 100.

Return value

Type Description
number Returns 0 by default.

Example

onProgress: (event, progress) => {
  L.i('P2PDemo', `${event === 0 ? 'Download' : 'Upload'}Progress: ${progress}%`)
  return 0
}

onFileProgress

Functional description

A callback for the progress of a single file transfer. It provides more granular progress information.

Function signature

onFileProgress(
  event: number,
  progress: number,
  filename: string
): number

Parameters

Parameter Type Description
event number The event type. Valid values:
  • 0: Download
  • 2: Upload
progress number The progress percentage, ranging from 0 to 100.
filename string The file name.

Return value

Type Description
number Returns 0 by default.

Example

onFileProgress: (event, progress, filename) => {
  L.i('P2PDemo', `${filename} ${event === 0 ? 'Download' : 'Upload'}Progress: ${progress}%`)
  return 0
}

onP2pResponse

Functional description

P2P response callback. It is used to receive response data for commands such as queries. This is the key callback for receiving file index query results.

Function signature

onP2pResponse(event: number, errCode: number, data: string): number

Parameters

Parameter Type Description
event number The event type. 12: Query file index.
errCode number The error code. 0 indicates success, and other values ​​indicate failure.
data string The response data in a JSON string.

Return value

Type Description
number Returns 0 by default.

Description of event types

Event value Description
12 The response for querying the file index.

Response data format (when event = 12)

interface P2PAlbumFileIndexs {
  count: number          // Total number of files
  items: FileIndex[]     // Array of file indices
}
  • This is the only callback that receives file index query results.
  • It is recommended to parse the data in this callback and directly call the download method.
  • The data needs to be parsed via JSON.parse().

Example

onP2pResponse: (event, errCode, data) => {
  L.i('P2PDemo', `P2P response: event=${event}, errCode=${errCode}`)

  // Handle the response to the file index query
  if (event === 12) {
    if (errCode === 0 && data) {
      try {
        const result: P2PAlbumFileIndexs = JSON.parse(data)
        L.i('P2PDemo', `Query was successful: count=${result.count}, items=${result.items.length}`)

        // 🔥 Call the download method here
        const savePath = '/data/storage/el2/base/haps/entry/files'
        const fileIndexesJson = JSON.stringify(result.items)
        p2pFileTransfer.startDownloadFiles('Camera', savePath, fileIndexesJson)

      } catch (err) {
        L.e('P2PDemo', `Parsing failed: ${err}`)
      }
    } else {
      L.e('P2PDemo', `Query failed: ${errCode}`)
    }
  }

  return 0
}

onStreamRecved

Functional description

A callback for streaming data reception. It is used for real-time data transmission such as maps. Data is returned packet by packet.

Function signature

onStreamRecved(
  event: number,
  sessionId: number,
  totalfiles: number,
  fileIndex: number,
  fileLength: number,
  pack: ArrayBuffer,
  packageLength: number,
  packageType: number
): number

Parameters

Parameter Type Description
event number Event types
sessionId number The session ID.
totalfiles number Total number of files.
fileIndex number The current file index.
fileLength number The total file length.
pack ArrayBuffer The payload.
packageLength number The packet length.
packageType number The packet type. 1: The new file starts.

Return value

Type Description
number Returns 0 by default.

Data packet types

Type value Description
1 New file start (first data packet).
Others Subsequent data packets.

Example

onStreamRecved: (event, sessionId, totalfiles, fileIndex, fileLength, pack, packageLength, packageType) => {
  if (packageType === 1) {
    L.i('P2PDemo', `Start receiving new files: file ${fileIndex}/${totalfiles}`)
  }

  L.d('P2PDemo', `Received data packet: length=${packageLength}, total length=${fileLength}`)

  // Process data packet...
  // For example: save to local, real-time rendering

  return 0
}