What this Skill does
Helps integrator developers correctly write integration code that calls the DJI Cloud API 2 Java SDK PSDK payload module:PsdkControlModule(34 methods) and PsdkResourceModule(7 methods).
What both have in common is that they only serve third-party PSDK payloads(searchlight, speaker, Widget, etc.), which are two completely different models from camera/gimbal (CameraControlModule):
- Different index types: the PSDK payload index is
int psdkIndex; the camera payload index isString("99-0-0") - Different transmission channels: control commands go through DRC, resource management goes through HTTP, and status push goes through DRC Northbound / WebSocket
- Different objects to obtain:
getPsdkControlModule()is onDrone, andgetPsdkResourceModule()is onWorkspaceUp
Core value: The most costly pitfall in this domain is not the API signature, but thecapability boundary—a batch of methods in the Java SDK are commented out or never implemented (real-time speaking, drop device), while the enum and class javadoc still "over-declare", so generating code literally will inevitably fail to compile or be unavailable at runtime.
When to trigger
- The user wants to write code to control the PSDK searchlight (on/off, brightness, mode, calibration, fine adjustment, wide angle, follow gimbal)
- The user wants to write code to control the PSDK speaker (play audio / TTS, replay, stop, volume, playback mode, TTS parameters)
- The user wants to subscribe to the PSDK device list, status summary, playback progress, floating window text, UI resources
- The user wants to set PSDK Widget values / input boxes, or batch obtain Widget resource URLs
- The user wants to upload speaker audio (STS credentials + direct upload to object storage) and perform resource CRUD
- The user encounters related errors:
psdkIndexnot knowing where it comes from, listen returning -1, callback receiving null, cancel failing to compile
Not triggered:
- The user wants tocall MCP Tool directly at runtime(
fh_searchlight/fh_speaker/fh_psdk_widget/fh_camera)→cloudapi2-mcp-guide - General SDK rules (initialization, authentication, callback order, listen/cancel pairing), flight control, waypoint missions, camera/gimbal control →
cloudapi2-sdk-core - Map domain (flight areas, offline maps, unlock certificates, map elements) →
cloudapi2-map - Device lifecycle, firmware upgrade, HMS alerts →
cloudapi2-device-management - SEI/OSD telemetry, video streams →
cloudapi2-sei-osd
Module location (confirm what it is attached to first)
// ✅ 控制模块在 Drone 上
PsdkControlModule control = drone.getPsdkControlModule();
// ✅ 资源模块在 Workspace 上
PsdkResourceModule resource = workspace.getPsdkResourceModule();
// ❌ 反过来取都不存在
// drone.getPsdkResourceModule() —— 不存在
// workspace.getPsdkControlModule() —— 不存在
| Module | Mounted object | Method count (verified from source) | Capabilities | Detailed documentation |
|---|---|---|---|---|
PsdkControlModule |
Drone |
34 | Searchlight / speaker / Widget / floating window / PSDK UI resources (DRC + DRC Northbound + WebSocket) | references/psdk-control.md |
PsdkResourceModule |
Workspace |
7 | Speaker resource CRUD / STS direct upload / Widget resources (HTTP) | references/psdk-resource.md |
The null-check behavior of the two differs:
workspace.getPsdkResourceModule()When the underlying module is missing,returnsnulland logsLogger.error→ must null-checkdrone.getPsdkControlModule()has no null return branch(if the underlying is null, construction fails) → null checks cannot defend against this; only ensure the business layer has performed PSDK checks
Core Rules
Rule 1: Control commands go through DRC and must first connectDrcLink
All 16 setters are delivered via DRC; the 6 DRC Northbound subscription groups also depend on the DRC link.
DrcModule drc = drone.getDrcModule();
drc.connectDrcLink(error -> {
if (error.getErrorCode() != FlightHubSdkErrorCode.SUCCESS) {
return; // 建链失败,不要继续下发控制指令
}
// 建链成功后才能调用 PsdkControlModule 的 setter / listen
});
// 退出时
drc.disconnectDrcLink(error -> { /* 记录结果 */ });
Don't miss this step—HTTP-type interfaces (PsdkResourceModule) do not need DRC, butafter creating a resource and going to play, it returns to DRC。
Rule 2: The Java SDK has no real-time voice broadcast and no payload dropper
| You think there is | actually |
|---|---|
speakerRealTimeStart / speakerRealTimeStop |
commented out and disabled in the source code. Real-time voice broadcast requires an RTC room + browser WebRTC + microphone capture; the Java side has no supporting capability |
| Payload dropper / dispenser control | The class javadoc mentions "payload control such as payload droppers", butthere is no corresponding method in the class |
Alternatives: for TTS use speakerTTSPlayStart, for audio files use speakerAudioPlayStart(first via PsdkResourceModule upload).
MegaphoneWorkModeThe enum still containsLIVE_VOICE(3)/REAL_TIME(4)/PLAYING_IN_TRANSMISSION(5), butno Java method can enter these modes. The existence of an enum value ≠ the existence of the capability.
Rule 3:psdkIndex is an int, from the state summary callback
int listListenId = control.listenDrcPsdkDeviceList(list -> {
if (list == null) { return; }
for (PsdkDeviceInfo device : list.getDeviceInfos()) {
int psdkIndex = device.getPsdkIndex(); // int
}
});
int summaryListenId = control.listenDrcPsdkStateSummaryInfo(psdkIndex, summary -> {
if (summary == null) { return; } // listenId == -1 时会收到 null
PsdkType type = summary.getPsdkType();
});
| Index | Type | Source |
|---|---|---|
| PSDK payload | int |
PsdkStateSummaryInfo.getPsdkIndex() / PsdkDeviceInfo.getPsdkIndex() |
| Camera payload | String |
"type-subtype-gimbalindex", such as "99-0-0" |
Do not guess psdkIndexbased on device order, and do not interchange it with the camera payload index.
Rule 4: All listen methods take a single parameter with no error; failures are detected via return values and null checks
| Family | Behavior on failure |
|---|---|
| DRC Northbound (6) | in catchwith null callback once with the converted bean, returns -1 |
| WebSocket (3) | in catchonly logs, does not call back, returns -1 |
int listenId = control.listenDrcPsdkStateSearchlightInfo(psdkIndex, info -> {
if (info == null) {
return; // 订阅失败路径
}
// 正常推送
});
if (listenId < 0) {
// 统一处理订阅失败(尤其 WebSocket,回调不会来)
}
Rule 5: The cancel signatures fall into two categories, with different parameter orders
// 带 psdkIndex 的(6 个 DRC 状态类):psdkIndex 在前,listenId 在后
control.cancelListenDrcPsdkStateSummaryInfo(psdkIndex, listenId);
control.cancelListenDrcPsdkStateSpeakerInfo(psdkIndex, listenId);
control.cancelListenDrcPsdkStateSearchlightInfo(psdkIndex, listenId);
control.cancelListenDrcPsdkSpeakerPlayProgressInfo(psdkIndex, listenId);
control.cancelListenDrcPsdkFloatingWindowTextInfo(psdkIndex, listenId);
// 只有 listenId 的(设备列表 + 3 个 WebSocket)
control.cancelListenDrcPsdkDeviceList(listenId);
control.cancelListenPsdkSpeakerAudioPlayStartProgress(listenId);
control.cancelListenPsdkSpeakerTtsPlayStartProgress(listenId);
control.cancelListenDronePsdkUiResource(listenId);
All cancel methods return void; internal exceptions are only logged,there is no error signal. You must save each listenId yourself and cancel it in pairs with @PreDestroy .
Rule 6: The input parameter types are asymmetric; do not copy them blindly
| Method | Should pass | Common mistake |
|---|---|---|
searchlightModeSet(psdkIndex, mode, cb) |
int(SearchLightWorkMode.getValue()) |
Passing an enum object → compilation failure |
speakerPlayModeSet(psdkIndex, playMode, cb) |
MegaphonePlayMode enum |
Passing an int → compilation failure |
speakerTTSSet(psdkIndex, config, cb) |
PsdkSpeakerTTSconfig |
Concatenating int parameters yourself |
deletePsdkSpeakerResource(resourceId, cb) |
int |
Passing a String |
EditPsdkSpeakerResourceParams.setId(...) |
String |
Passing an int |
Two other bean field naming pitfalls:PsdkSpeakerResourceList uses getResources(), while PsdkWidgetResourceList uses getUiResourceUrls();
PsdkUiResourceItem.getPsdkIndex() returns long, while other PSDK beans are all int。
Rule 7: Audio resources follow a four-step process, and each step has its own point of failure
getSTS() → STSInfo(临时凭证,会过期)
↓
uploadSpeakerAudioFile(path, stsInfo) → 仅 error;SDK 内部直传对象存储,不经过后端
↓
createPsdkSpeakerResource(params) → 登记元数据,得到 int id
↓
connectDrcLink → speakerAudioPlayStart → 在 PsdkControlModule 上播放
create is not an upload;upload does not require a prior create; do not cache STS credentials long-term.
Decision Tree Routing
用户问题
├─ 探照灯:开/关/亮度/模式/校准/微调/广角/随云台/灯类型
│ → references/psdk-control.md#3-探照灯控制drc7-个方法
├─ 喊话器:播放音频/TTS/重播/停止/音量/播放模式/TTS 参数
│ → references/psdk-control.md#2-喊话器控制与-widgetdrc9-个方法
├─ 实时喊话 / 边喊边播 / 对讲机模式
│ → 本文件「规则 2」(Java SDK 不支持,给替代方案)
├─ PSDK 设备发现 / 状态摘要 / 状态子集(喊话器、探照灯)
│ → references/psdk-control.md#1-psdk-设备发现与状态订阅drc-northbound6-组
├─ 播放进度 / 浮窗文本 / UI 资源订阅
│ → references/psdk-control.md#2-喊话器控制与-widgetdrc9-个方法 / #5-widget--输入框--ui-资源
├─ PSDK Widget 数值 / 输入框文本设置
│ → references/psdk-control.md#5-widget--输入框--ui-资源
├─ 音频资源上传(STS / 直传)/ 资源增删改查 / 资源列表
│ → references/psdk-resource.md
├─ Widget 资源 objectKey → 可访问 URL
│ → references/psdk-resource.md#3-widget-资源批量查询
├─ psdkIndex 从哪来 / 类型不对 / 和相机索引混用
│ → 本文件「规则 3」+ references/psdk-control.md#psdkindex-是什么
├─ listen 返回 -1 / 回调收到 null / cancel 编译不过
│ → 本文件「规则 4、5」
├─ 指令下发无反应
│ → 本文件「规则 1」(先检查 connectDrcLink)
└─ 要调 MCP 工具做运维(不写代码)
→ 转交 cloudapi2-mcp-guide
Workflow
This Skill follows the 7-chapter SOP defined by skills/_shared/sop-template.md , automatically selecting the level based on L1/L2/L3.
Ch2 Clarifying Questions
Points that must be clarified for PSDK payload requirements (missing any one may lead to incorrect implementation):
- Payload type: searchlight / speaker / Widget, or just querying the device list (determines which status group to subscribe to)
psdkIndexwhether it is known: if unknown, first write device discovery + status subscription; do not hard-code 0- Audio source: TTS text, an existing resource ID, or a local file (local files go through the four-step STS upload process)
- Whether the DRC link has been established: control-type requirements must first
connectDrcLink(HTTP resource management does not require it) - Whether the runtime environment is server-side or browser: real-time speaking can only be implemented on the browser side; the server side can only provide TTS / audio playback
Ch5 Coding Constraints
See constraints.yaml; all critical rules must be satisfied when generating code; general SDK rules (initialization, authentication, null checks,
listen/cancel pairing) reference _shared/constraints-common.yamland are not repeated in this file.
Ch6 Verification Extension
In addition to general verification, code in this domain must check:
- the module getter uses the correct object (control ->
drone, resource ->workspace), and the resource module has been null-checked - before control-type calls there is a
connectDrcLinksuccess branch, and on exit there isdisconnectDrcLink - no
speakerRealTimeStart/speakerRealTimeStop/ dropper-related API is generated -
psdkIndexcomes from the status callback (int), and is not mixed with the camera payload indexStringMixed use - all listen callbacks aresingle-parameter, and the DRC Northbound callback body has
nullnull checks - each
listenIdhas been saved and paired with cancel, and the cancel parameter count/order matchesreferences/consistent -
searchlightModeSetpasses int,speakerPlayModeSetpasses enum, not reversed - the audio upload process includes three abort branches:
getSTSfailure, upload failure, and creation failure - the Widget resource list is read using
getUiResourceUrls()read
Anti-hallucination rules (mandatory)
- Example code must come from this Skill's references/ or user-provided documentation, and API signatures, field names, and enum values must not be generated from model memory
- It is forbidden to infer APIs from enums or javadoc—this domain has confirmed three types of over-declaration: "enum has a value but no method" (
MegaphoneWorkMode3/4/5), "declared in javadoc but not implemented" (dropper), and "source comment disabled" (real-time speaking) - References must be traceable—any suggestion must be traceable to a
references/documentation path or official SDK source code (core/PsdkControlModule.java、core/PsdkResourceModule.java)
Authoritative references
| Document | Content |
|---|---|
references/psdk-control.md |
PsdkControlModule All 34 methods: DRC link-establishment prerequisites, 6 groups of DRC Northbound subscriptions, 3 groups of WebSocket subscriptions, 16 setters, bean/enum and input parameter type comparison |
references/psdk-resource.md |
PsdkResourceModule All 7 methods: resource CRUD, the four-step STS direct upload process, Widget resource batch query, bean field table |
skills/_shared/constraints-common.yaml |
Cross-Skill general constraints (callbacks/initialization/null checks) |
Shared specifications
For cross-Skill shared constraints, see
skills/_shared/constraints-common.yaml. For cross-Skill routing rules, seeskills/_shared/skill-routing.md. When it is found that the user's intent falls under the responsibility of another Skill, switch according to the handover protocol. For the output format, seeskills/_shared/output-format.md. References are lazily loaded as needed; do not read all references/ at once.