Local SDK User Management
SDK Practice
Overview
Hand model and tactile calibration data belongs to the current SDK user. When several people share one glove on the same computer, create a separate user for each person to keep calibration data separate.
User information is stored locally and is not uploaded. The current user is written to a configuration file shared by Python and C, so a switch takes effect in other processes immediately.
Confirm the current user before calibration. This page covers user creation, switching, deletion, and data transfer.
Matching examples: examples/python/wuji_glove/4.user.py and examples/c/wuji_glove/3_user_management.c.
For API and field definitions, see SDK User Management and User Data Import and Export.
Prerequisites
Hardware
User management reads and writes local data and doesn’t need a glove.
Software
| Item | Requirement |
|---|---|
| Operating system | Linux or a Mac with Apple silicon, meeting the system requirements |
| Wuji SDK | v2026.8.31 or later |
| Python | 3.8 or later |
| C toolchain | CMake 3.16 or later, plus the extracted SDK package |
Python
C
pip install wuji-sdkLIB="libwuji_sdk_c.so"
if [ "$(uname -s)" = "Darwin" ]; then LIB="libwuji_sdk_c.dylib"; fi
cmake -S examples/c/wuji_glove -B build/glove \
-DWUJI_SDK_INCLUDE_DIR="${SDK}/include" \
-DWUJI_SDK_LIB="${SDK}/lib/${LIB}"
cmake --build build/glove --target 3_user_management Data Flow
Local Storage Layout
The SDK uses ~/.wuji/sdk/ for the following data:
~/.wuji/sdk/
├── storage.json # SDK-level state
├── users/
│ ├── registry.toml # user registry plus the current-user pointer
│ └── <user_id>/ # each user's private directory
├── params/
│ └── <serial>.toml # parameters filed per glove
└── models/
└── <serial>_hand.urdf # the hand model calibration produces Example registry.toml:
current_user_id = "u_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
[[users]]
user_id = ""
display_name = "Default"
is_default = true
created_at = "2026-07-14T13:05:53.428368166+00:00"
updated_at = "2026-07-14T13:05:53.428368166+00:00"
[[users]]
user_id = "u_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
display_name = "alice"
is_default = false
created_at = "2026-07-14T13:05:53.428376205+00:00"
updated_at = "2026-07-14T13:05:53.428376205+00:00" The default user’s user_id is an empty string, not "default" or None. current_user_id is stored in this file, so user switching persists.
API Reference
The user management interfaces are:
| Purpose | Python | C |
|---|---|---|
| Read the current user | current_user() | wuji_current_user(&out) |
| List them all | list_users() | wuji_list_users(&arr, &len) |
| Create one | create_user(display_name, description=None) | wuji_create_user(name, desc, external_id, &out) |
| Update details | update_user(user_id, description=...) | wuji_update_user(user_id, &update, &out) |
| Switch | switch_user(user_id) | wuji_switch_user(user_id, &out) |
| Switch back to default | switch_to_default_user() | wuji_switch_to_default_user(&out) |
| Delete | delete_user(user_id) | wuji_delete_user(user_id) |
| Export a calibration bundle | export_user_data(user_id, out_path) | wuji_user_data_export(user_id, out_zip_path) |
| Preview a bundle | preview_user_data(bundle_path) | wuji_user_data_preview(zip_path) |
| Import a bundle | import_user_data(...) | wuji_user_data_import(zip_path) |
Python and C provide equivalent user management capabilities. Use the export, preview, and import interfaces to move calibration data between computers.
On the Python side, the user dict holds user_id, display_name, description, is_default, created_at, updated_at, and external_id. The C WujiUserInfo struct uses the same field names.
The SDK allocates strings in WujiUserInfo output values. Free a single structure with wuji_user_info_free(&user) and an array with wuji_user_info_array_free(users, len). Do not mix these functions.
Besides display_name, description, and external_id, WujiUserUpdate carries the booleans clear_description and clear_external_id for setting a field to empty. Passing NULL means “leave it alone,” not “clear it.”
Integration Steps
Confirm the current user, select or create a user, then check the switch result. User operations don’t require a connected device.
1. Read the Current User
Read the current user to confirm who will own subsequent calibration data.
Python
C
from wuji_sdk import SdkManager
manager = SdkManager.instance()
previous = manager.current_user()WujiInitOptions opts = { .log_level = 2 };
if (wuji_init(&opts) != WUJI_STATUS_OK) return 1;
WujiUserInfo previous = {0};
if (wuji_current_user(&previous) != WUJI_STATUS_OK) {
fprintf(stderr, "%s\n", wuji_last_error());
wuji_shutdown();
return 1;
} 2. Select or Create a User
Find users by display name. Reuse an existing user, or create one if none matches. Python’s find_user_by_display_name and C’s switch_or_create_user and ExampleArgs come from Python example and C example. They aren’t SDK APIs. Multiple users with the same name cause an error. Resolve duplicates before switching.
Python
C
name = "Alice"
user = find_user_by_display_name(manager, name)
if user is None:
user = manager.create_user(name, description="right hand")ExampleArgs args = {
.user_name = "Alice",
.user_description = "right hand",
};
WujiUserInfo current = {0};
if (switch_or_create_user(&args, ¤t) != 0) {
wuji_user_info_free(&previous);
wuji_shutdown();
return 1;
} The C helper calls wuji_list_users, then wuji_create_user or wuji_update_user as needed, followed by wuji_switch_user. It frees the query list and temporary user information and returns the selected current user.
3. Switch and Confirm
Python switches by user_id. C has already switched in the previous step. Check the returned user information before calibration. The selected user persists and is used by subsequent processes.
Python
C
current = manager.switch_user(user["user_id"])
print(current["display_name"], current["user_id"])printf("%s (%s)\n", current.display_name, current.user_id); 4. Release Resources
Python manages user dictionaries at runtime. In C, free user information and shut down the SDK when the program ends. Freeing query results doesn’t delete users or calibration data.
wuji_user_info_free(&previous);
wuji_user_info_free(¤t);
wuji_shutdown(); Run the Examples
Python
C
python examples/python/wuji_glove/4.user.py --list
python examples/python/wuji_glove/4.user.py --user-name Alice --user-description "right hand"
python examples/python/wuji_glove/4.user.py --default-user./build/glove/3_user_management --list
./build/glove/3_user_management --user-name Alice --user-description "right hand"
./build/glove/3_user_management --default-user Measured output from the C side’s --list:
previous current user:
user_id: 'u_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
display_name: alice
description: None
is_default: False
current SDK user:
user_id: 'u_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
display_name: alice
description: None
is_default: False
SDK users:
Default user_id='' description=None
* alice user_id='u_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' description=None When you create a user, the previous and current blocks differ:
previous current user:
display_name: alice
current SDK user:
user_id: 'u_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'
display_name: CDocTest
description: verified from C
is_default: False Both sides print identical formats, which the examples align deliberately. The * marks the current user.
These are real results from SDK v2026.8.31 with no device connected. Every user created during verification has been deleted, and the current user is back to what it was.
Behavior and Error Handling
The following behavior applies to the user management interfaces.
User Names Must Be Unique
WujiException: Internal error: SDK user display_name 'PracticeDoc' already exists The SDK guarantees unique display_name values. The duplicate-match branch in the example is not reached:
matches = [u for u in manager.list_users() if u.get("display_name") == display_name]
if len(matches) > 1:
raise RuntimeError(f"Multiple SDK users named {display_name!r}. ...") Callers don’t need an additional branch for duplicate results.
Callers still need to handle a name collision during creation. Concurrent creates can race, so catch WujiException.
Deleting the Current User
After deletion, the SDK switches to the default user:
| Action | current_user() afterward |
|---|---|
Switch to PracticeDoc, then delete PracticeDoc | display_name='Default', user_id='', is_default=True |
The operation doesn’t leave a dangling current-user pointer. It also deletes that user’s calibration data, so confirm before deleting.
The Default User Can’t Be Deleted
WujiException: Internal error: default SDK user cannot be deleted A Nonexistent user_id Raises an Error
WujiException: Internal error: SDK user 'u_does_not_exist' not found The SDK doesn’t switch to the default user automatically.
Switching Takes Effect Across Processes and Languages
For example:
- The C program runs
./3_user_management --default-user, switching to Default - A brand-new Python process reads
current_user()
The result is Default with a user_id of ''. Going the other way, a user created by the C program’s --user-name CDocTest shows up in Python’s list_users() immediately, with non-ASCII text in description intact:
user list: [('Default', None), ('alice', None), ('CDocTest', 'verified from C')] Both interfaces read and write the same registry.toml and don’t maintain separate caches.
Export Requires Calibration Data
Calling export_user_data for a user without calibration data:
WujiException: Internal error: no calibration data found for user alice Both arguments in export_user_data(user_id, out_path) are required.
Error Handling Quick Reference
| Branch point | Behavior | What to do |
|---|---|---|
| display_name collides at creation | Raises WujiException | Check with list_users() first, or catch the exception and switch instead |
| Deleting the current user | Allowed, falls back to the default user | Confirm you no longer need the calibration data |
| Deleting the default user | Raises WujiException | The default user is the fallback and can’t be removed |
| Switching to a nonexistent user_id | Raises WujiException | Verify against list_users() |
| Exporting an uncalibrated user | Raises WujiException | Calibrate first |
| Several processes changing users at once | Last write wins | Switching users is infrequent, so avoid concurrency |
Command-line tools can catch WujiException and exit with a non-zero status. Display the exception message without printing the full traceback.
Usage Recommendations
Confirm the User Before Calibrating
This is the whole reason for this page. Calibration output lands under the current user, so the wrong user overwrites someone else’s data, silently. Add one line at the top of your calibration script:
print(f"calibration will write to user: {SdkManager.instance().current_user()['display_name']}") Move Calibration Results to Another Machine
export_user_data packs the bundle, preview_user_data shows you what’s inside on the target machine, and import_user_data brings it in once you’re satisfied. Importing recreates the user as needed, so you don’t have to create a matching user by hand first.
Use external_id to Link an External System
The user dict carries an external_id field that neither example uses. When you connect your own account system, store your external primary key there instead of matching on display_name.
Keep Scripts Non-Interactive
Both examples are one-shot command-line tools, which suits dropping them into the shell script around your calibration flow. --list exits 0 on success and 1 on failure, so you can branch on that.
Related
- Wuji Glove Hand Model Calibration: produces the hand model filed under the current user
- Wuji Glove Tactile Calibration: also filed by user and serial number
- Wuji Glove Custom Hand Model: name a hand model directly, bypassing the user system
- Wuji Glove Offline Processing: loads the hand model by serial number during replay
ドキュメント