aqara-open-api
Query and control Aqara/Lumi Studio smart home devices and manage spaces via the Aqara Open Platform API (HTTP JSON). Use when the user asks to list Aqara devices, get device status, control lights/switches/sensors, or manage rooms/spaces. Requires AQARA_OPEN_API_TOKEN and AQARA_ENDPOINT_URL.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install openclaw-skills-aqara-open-api
Repository
Skill path: skills/aiot-f2e-team/aqara-open-api
Query and control Aqara/Lumi Studio smart home devices and manage spaces via the Aqara Open Platform API (HTTP JSON). Use when the user asks to list Aqara devices, get device status, control lights/switches/sensors, or manage rooms/spaces. Requires AQARA_OPEN_API_TOKEN and AQARA_ENDPOINT_URL.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Backend.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: openclaw.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install aqara-open-api into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/openclaw/skills before adding aqara-open-api to shared team environments
- Use aqara-open-api for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: aqara-open-api
description: Query and control Aqara/Lumi Studio smart home devices and manage spaces via the Aqara Open Platform API (HTTP JSON). Use when the user asks to list Aqara devices, get device status, control lights/switches/sensors, or manage rooms/spaces. Requires AQARA_OPEN_API_TOKEN and AQARA_ENDPOINT_URL.
version: 1.0.1
author: aqara
homepage: https://opendoc.aqara.com
metadata: {"clawdbot":{"emoji":"🏠","requires":{"bins":["jq","curl"],"env":["AQARA_ENDPOINT_URL","AQARA_OPEN_API_TOKEN"]}}}
---
# Aqara Open API Skill
This skill equips the agent to operate Aqara/Lumi Studio smart home devices via HTTPS requests to the Aqara Open Platform API. **All operations are performed exclusively through `curl` commands**, except `GetAllDevicesWithSpaceRequest`, which must be executed through `bash scripts/fetch_all_devices.sh` to refresh the local cache file `data/devices.json`.
This skill supports only:
- device discovery and device type lookup
- device state queries from cache
- device control
- space listing, creation, update, and device assignment
## Configuration
The following environment variables are required:
- `AQARA_ENDPOINT_URL`: The base URL
- `AQARA_OPEN_API_TOKEN`: Your Long-Lived Access Token.
## AI Quick Navigation (Read This First)
> This section is a navigation and execution summary only. It **does not add new rules** or change existing constraints.
### What This Skill Can Do
- **Device discovery**: load all devices, space mappings, endpoints, functions, traits, and current values
- **Device type catalog**: query all device types in the project with code and display name
- **Device queries**: filter by type, name, room/space, or online status; read current trait values
- **Device control**: send control requests using real `deviceId`, `endpointId`, `functionCode`, and `traitCode` from cache
- **Space management**: list spaces, create spaces, update spaces, and assign devices to spaces
### Intent to Fastest Path
- **List all devices / devices by type / devices in a room / device state**
- Check `data/devices.json`
- If cache exists: read the file
- If cache is missing: run `bash scripts/fetch_all_devices.sh`
- **Control a device**
- Ensure `data/devices.json` exists
- Read `deviceId + endpointId + functionCode + traitCode` from cache
- Then use bash + `curl` for `ExecuteTraitRequest`
- **What device types are there?**
- Use bash + `curl` for `GetDeviceTypeInfosRequest`
- **Refresh all device data**
- Only run `bash scripts/fetch_all_devices.sh`
- **List spaces**
- Use bash + `curl` for `GetSpacesRequest`
- **Create or update a space**
- First get the real `spaceId` from `GetSpacesRequest`
- Then call `CreateSpaceRequest` or `UpdateSpaceRequest`
- **Assign devices to a space**
- First get the real `spaceId` from `GetSpacesRequest`
- Read real `deviceId` values from `data/devices.json`
- Then call `AssociateDevicesToSpaceRequest`
### Six Highest-Priority Rules
1. **All-device loading only goes through the script**: `GetAllDevicesWithSpaceRequest` may only be executed via `bash scripts/fetch_all_devices.sh`.
2. **All other requests only go through curl**: except for the refresh script above, every other API call must use bash + `curl`.
3. **Request bodies may only contain four fields**: `type`, `version`, `msgId`, and `data`; **`version` must always be `"v1"`**.
4. **`type` is whitelist-only**: use only the exact request types listed in this document; never test guessed alternatives such as `GetAllSpacesRequest`, `GetSpaceListRequest`, or `QuerySpaceListRequest`.
5. **All IDs and codes must come from real API data**: never guess `deviceId`, `endpointId`, `functionCode`, `traitCode`, or `spaceId`.
### Required Preconditions
- **Before listing devices, reading state, or filtering by room/type**: check whether `data/devices.json` exists
- **Before controlling a device**: obtain the real `deviceId`, `endpointId`, `functionCode`, and `traitCode` from the cache file
- **Before creating or updating a space**: if `spaceId` is needed, call `GetSpacesRequest` first
- **Before assigning devices to a space**: get `spaceId` from `GetSpacesRequest` and `deviceId` values from `data/devices.json`
### Terms and Field Reference
- **Cache file**: `data/devices.json`, which stores the `data` array from `GetAllDevicesWithSpaceRequest`
- **deviceId**: device identifier used for control and space assignment
- **endpointId**: endpoint identifier, only from cached `endpoints[].endpointId`
- **functionCode / traitCode**: capability identifiers from cache
- **spaceId**: space identifier from `GetSpacesRequest` or cached `space.spaceId`
### Section Index
- **Protocol and four-field request body**: see `## API Protocol`
- **Script vs curl execution rules**: see `### Execution Model`
- **Cache file rules**: see `### File Cache Model (Cache-First Data Model)`
- **Device APIs**: see `## API Commands` under `### Device Management`
- **Space APIs**: see `### Space Management`
- **Standard workflows**: see `## Standard Operating Procedures (SOP)`
- **Decision trees**: see `## Cache Decision Tree` and `## API Call Decision Tree`
- **Forbidden behavior**: see `## Forbidden Behavior`
- **Trait code reference**: see `references/trait-codes.md`
## 1. Role and Core Philosophy
**Role**: You are a strict hardware interface controller. Never infer or guess IDs or capability fields.
## 2. Hard Safety Rules
### 2.1 Valid Value Rule
Any operation involving live device state, such as power, brightness, or temperature, must follow this rule:
| Power status | Valid interpretation | Response style |
|-------------|----------------------|----------------|
| `Switch == "false"` | Brightness / color temperature / power should be treated as 0 or "off" | "The device is currently off" |
| `Switch == "true"` | Use the actual returned value | "Brightness is X%" |
**Never** produce logically inconsistent output such as "the light is off but brightness is 100%".
## Quick Start (Operator)
1. Set environment variables:
- `AQARA_OPEN_API_TOKEN`: your Aqara Open API Bearer token (JWT)
- `AQARA_ENDPOINT_URL`: the API base URL
**Real environment value rule**: `AQARA_ENDPOINT_URL` and `AQARA_OPEN_API_TOKEN` **must be read from the runtime environment** (via `$AQARA_ENDPOINT_URL` and `$AQARA_OPEN_API_TOKEN`). **Do not guess, fabricate, or use example placeholders** as real request values. If either variable is missing or empty, tell the user to configure it first.
2. Test connectivity:
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"GetAllDevicesWithSpaceRequest","version":"v1","msgId":"test-1"}'
```
## API Protocol
Base URL: `$AQARA_ENDPOINT_URL`
All requests use a **single unified POST endpoint**. Routing is determined by the `type` field in the JSON body.
### Request Envelope
The request-body JSON may contain **exactly these 4 fields**. Do not add, remove, or replace fields:
```json
{
"type": "<RequestType>",
"version": "v1",
"msgId": "<unique-id>",
"data": { ... }
}
```
| Field | Required | Meaning | Forbidden behavior |
|------|----------|---------|--------------------|
| `type` | Yes | API method name, such as `ExecuteTraitRequest` or `GetSpacesRequest` | Do not modify, abbreviate, or replace it with a value not listed in this document |
| `version` | Yes | **Must be the string `"v1"`** | Do not omit it; do not use `"v2"`, `"1.0"`, or any other value |
| `msgId` | Yes | Unique request identifier, such as `"msg-1234567890"` | None |
| `data` | Yes | Payload for the selected `type` (`null`, object, array, or string depending on the request) | Only use structures defined in this document or `references/` |
**Strict request-body constraints**
1. **Do not omit `version`**: every curl request body must include `"version":"v1"`.
2. **Do not add undefined fields**: the request body may contain **only** these 4 keys. Do not add `senderId`, `source`, `timestamp`, or any field not defined in this document.
3. **Do not invent field names or structures**: neither the top-level body nor `data` may contain made-up fields or undocumented shapes.
4. **Do not invent or trial-run alternate `type` values**: if this document says the request type is `GetSpacesRequest`, then `type` must be exactly `GetSpacesRequest`.
5. **Do not use synonym or variant guessing for `type`**: names such as `GetAllSpacesRequest`, `GetSpaceListRequest`, `QuerySpaceListRequest`, `ListSpacesRequest`, or other self-created variants are forbidden unless they are explicitly documented in this file.
### Required Headers
```
Authorization: Bearer $AQARA_OPEN_API_TOKEN
Content-Type: application/json
```
### Response Envelope
```json
{
"type": "<ResponseType>",
"version": "v1",
"msgId": "<matching-id>",
"code": 0,
"message": null,
"data": { ... }
}
```
`code: 0` = success. Non-zero = error. Common error codes:
- 400: invalid parameters
- 1001: token expired
- 2030: device not found
### curl Template
The `-d` value for every curl request must be a JSON object with **only 4 keys**: `type`, `version`, `msgId`, and `data`. Replace `<TYPE>` with the API method name and `<DATA>` with the correct payload:
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"<TYPE>","version":"v1","msgId":"msg-'$(date +%s)'","data":<DATA>}'
```
Always keep `"version":"v1"` in `-d`, and never add `senderId` or any other field not defined in this document.
## How the Agent Should Use This Skill
### Execution Model
**Execution rules (must follow)**:
- **Only `GetAllDevicesWithSpaceRequest`**: execute it via **`bash scripts/fetch_all_devices.sh`**. The script calls the API and writes the response `data` to `data/devices.json`. Do not call this request directly with curl.
- **All other supported API requests** (such as `ExecuteTraitRequest`, `GetSpacesRequest`, `CreateSpaceRequest`, `UpdateSpaceRequest`, and `AssociateDevicesToSpaceRequest`): execute them with **bash + `curl`**. The JSON body must contain **exactly 4 keys**: `type`, `version`, `msgId`, and `data`.
- **No exploratory `type` retries**: when a request fails, do not try nearby or similar type names. Verify the documented `type`, payload shape, token, IDs, cache, and required preconditions, then retry the same documented request only if appropriate.
### File Cache Model (Cache-First Data Model)
**Local cache file**
- `data/devices.json` — the `data` array from `GetAllDevicesWithSpaceRequest`, including complete device information: `deviceId`, `name`, `deviceTypesList`, `state`, `space`, `endpoints`, and all `functions/traits/current values`
**Refresh cache (create/overwrite cache file)**
```bash
bash scripts/fetch_all_devices.sh
```
**Usage rules (must follow)**
1. **Read cache first**: when the user asks for device lists, device types, room mappings, or basic device information, check whether `data/devices.json` exists and is non-empty.
2. **Cache hit**: if the cache file exists, **read the file directly** and continue. Do not call `GetAllDevicesWithSpaceRequest` again.
3. **Cache miss / missing file / parse failure**: run `bash scripts/fetch_all_devices.sh` to refresh the cache. After refresh, **re-read the cache file** before continuing.
4. **Explicit refresh requested by the user**: run `bash scripts/fetch_all_devices.sh` and then re-read the file.
5. **Control commands** (`ExecuteTraitRequest`) still go directly to the API via curl, but all `deviceId` / `endpointId` / `functionCode` / `traitCode` values **must come from the cache file**.
### Critical Data-Only Rule
**NEVER guess, fabricate, or infer** `deviceId`, `endpointId`, `functionCode`, or `traitCode`. Every value used in control calls **MUST** come from the cached `data/devices.json` file.
- `endpointId` must be taken from the device's `endpoints[].endpointId`.
- `functionCode` and `traitCode` must be taken from `endpoints[].functions[].traits[]`.
- `value` type must match the trait definition.
### Local Filtering (No Extra API Calls)
When the user asks for a subset of devices (for example, all lights, devices in the bedroom, or online switches):
- **Filter the cached `data/devices.json` locally** by `deviceTypesList`, `name`, `state`, or `space.name` / `space.spatialMarking`.
- Do **not** make a new API call to filter.
### Device Type vs Device Name
- **By Type (preferred)**: when the user asks for lights or switches, filter cached file by `deviceTypesList`.
- **By Name (secondary)**: use device name only to select a specific device from the cached list.
### Error Handling
- On errors with code 1001 (token expired) or 400 (bad params): re-check token, verify all IDs come from cached device data.
- On 2030: device not found; run `bash scripts/fetch_all_devices.sh` to refresh cache and retry.
- Timeout/network errors: retry once, then report.
- Cache file parse error: delete `data/devices.json` and run `bash scripts/fetch_all_devices.sh` to regenerate.
## API Commands (7 total)
### Device Management
#### Get all devices with space info — `GetAllDevicesWithSpaceRequest`
Retrieve all smart home devices in a single call, including full device info, space assignment, endpoints, functions, traits, and current values. No `data` field is needed (or `data: null`).
The agent **MUST** write this response to the local cache file (`data/devices.json`) and use the cache for all subsequent device queries, status checks, and as the basis for control commands. Use the provided script to fetch and cache:
```bash
bash scripts/fetch_all_devices.sh
```
Manual curl (for reference only — prefer the script):
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"GetAllDevicesWithSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'"}'
```
#### Get all device types — `GetDeviceTypeInfosRequest`
Retrieve every device type available in the current project. Each entry contains a `deviceType` code and its localized display `name`.
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"GetDeviceTypeInfosRequest","version":"v1","msgId":"msg-'$(date +%s)'"}'
```
#### Control device — `ExecuteTraitRequest`
Control one or more device functions, such as turning on/off or adjusting level. `endpointId`, `functionCode`, and `traitCode` must be read from the cache.
```bash
# Turn on
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"ExecuteTraitRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":[{"deviceId":"<deviceId>","endpointId":<endpointId>,"functionCode":"<functionCode>","traitCode":"OnOff","value":true}]}'
# Turn off
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"ExecuteTraitRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":[{"deviceId":"<deviceId>","endpointId":<endpointId>,"functionCode":"<functionCode>","traitCode":"OnOff","value":false}]}'
```
### Space Management
#### List space hierarchy — `GetSpacesRequest`
Retrieve all spaces as a hierarchical tree. Each space includes its ID, name, parent space ID, spatial marking label, and nested children.
**Space request type hard rule**: the request `type` for space listing is **only** `GetSpacesRequest`. Do not try `GetAllSpacesRequest`, `GetSpaceListRequest`, `QuerySpaceListRequest`, or any other guessed variant.
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"GetSpacesRequest","version":"v1","msgId":"msg-'$(date +%s)'"}'
```
#### Create a new space — `CreateSpaceRequest`
Create a room, zone, or building. Omit `parentSpaceId` to create a top-level space. Run `GetSpacesRequest` first if the parent space ID is not known.
```bash
# Top-level space
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"CreateSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"name":"Living Room","spatialMarking":"living_room"}}'
# Sub-space
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"CreateSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"name":"Bedroom","parentSpaceId":"<parentSpaceId>","spatialMarking":"bedroom"}}'
```
#### Update space properties — `UpdateSpaceRequest`
Update the name or spatial marking of an existing space. Only provided fields are updated. `spaceId` is required.
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"UpdateSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"spaceId":"<spaceId>","name":"New Room Name"}}'
```
#### Assign devices to a space — `AssociateDevicesToSpaceRequest`
Assign one or more devices to an existing space. Response `data` only contains failed items; empty means all succeeded.
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"AssociateDevicesToSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"spaceId":"<spaceId>","deviceIds":["<deviceId1>","<deviceId2>"]}}'
```
## Standard Operating Procedures (SOP)
### SOP A: List or discover devices
1. Check whether `data/devices.json` exists and is non-empty.
2. If it exists: read device data directly from the file.
3. If it is missing or empty: run `bash scripts/fetch_all_devices.sh` to generate the cache, then read the file.
4. Answer the user's request by filtering the cache file.
### SOP B: Query device state
1. Device state and trait values are already included in the cache file.
2. If `data/devices.json` is missing, run `bash scripts/fetch_all_devices.sh` first.
3. Find the device in the cache file and read the corresponding trait `value`.
4. No separate API call is needed to read device state.
### SOP C: Control device
1. If `data/devices.json` is missing, run `bash scripts/fetch_all_devices.sh` first.
2. Read the exact `deviceId`, `endpointId`, `functionCode`, and `traitCode` from the cache file.
3. Use the `ExecuteTraitRequest` curl command with only cached values.
4. Response `data` only contains failed items; an empty array means all commands succeeded.
### SOP D: Space management
1. Device-to-space associations are already included in `data/devices.json`.
2. Use the `GetSpacesRequest` curl command to retrieve the full space hierarchy tree.
3. Use `CreateSpaceRequest`, `UpdateSpaceRequest`, or `AssociateDevicesToSpaceRequest` as needed.
## Cache Decision Tree
```
Does the user request involve devices, spaces, or types?
├── Yes → Check the local cache file `data/devices.json`
│ ├── Cache hit (file exists and is non-empty)
│ │ └── Read from the file directly and filter/aggregate locally
│ └── Cache miss / missing file
│ ├── Run `bash scripts/fetch_all_devices.sh`
│ ├── Re-read the cache file
│ └── Continue with the refreshed data
└── No → Continue with the normal flow
Does the task require device control?
├── Yes → Ensure `data/devices.json` exists (refresh first if missing)
│ ├── Read `deviceId + endpointId + functionCode + traitCode` from cache
│ └── Call `ExecuteTraitRequest` via curl
└── No → Return the cached data result
Did the user explicitly ask to refresh device data?
└── Run `bash scripts/fetch_all_devices.sh` to overwrite the cache, then re-read it
```
## API Call Decision Tree
```
User wants devices by type?
→ data/devices.json exists? → Yes: filter file by deviceTypesList
→ No: bash scripts/fetch_all_devices.sh, then filter
User wants to list/discover all devices?
→ data/devices.json exists? → Yes: return full list from file
→ No: bash scripts/fetch_all_devices.sh, then read
User wants state of a device?
→ data/devices.json exists? → No: bash scripts/fetch_all_devices.sh
→ Read value from file: endpoints[].functions[].traits[].value
User wants to control a device?
→ data/devices.json exists? → No: bash scripts/fetch_all_devices.sh
→ Get deviceId + endpointId + functionCode + traitCode from file
→ curl ExecuteTraitRequest with correct value type
User wants devices by space/room?
→ data/devices.json exists? → Yes: filter file by space.name / spatialMarking
→ No: bash scripts/fetch_all_devices.sh, then filter
User wants to refresh device data?
→ bash scripts/fetch_all_devices.sh
User wants to see spaces?
→ curl GetSpacesRequest
User wants to create, update, or assign spaces?
→ curl GetSpacesRequest if a real spaceId is needed
→ then curl CreateSpaceRequest / UpdateSpaceRequest / AssociateDevicesToSpaceRequest
```
## Forbidden Behavior
| Forbidden | Correct behavior |
|-----------|------------------|
| Guessing or fabricating `deviceId` | Always use IDs from `data/devices.json` cache file. |
| Guessing or creating `endpointId` | Always take from `endpoints[].endpointId` in cache file. |
| Guessing `functionCode` or `traitCode` | Always take from `endpoints[].functions[].traits[]` in cache file. |
| Running `ExecuteTraitRequest` without first resolving device/endpoint info | Ensure `data/devices.json` exists and use the cached structure. |
| Using curl to call `GetAllDevicesWithSpaceRequest` | Only this request goes through the script: use `bash scripts/fetch_all_devices.sh`. |
| Calling the script or API for devices when cache file already exists and user did not ask to refresh | Read `data/devices.json` instead. |
| Making a separate API call to read device status/values | Device status and trait values are already in `data/devices.json`. |
| Inferring device type from device name | Filter cached devices by `deviceTypesList`. |
| Guessing `spaceId` | Always use IDs from `GetSpacesRequest` response or cached device `space.spaceId`. |
| Guessing or testing undocumented request `type` values | Use only the exact request names listed in `Request Type Whitelist`. |
| Trying alternate request names after a failure | Keep the documented `type` unchanged; inspect payload, token, IDs, cache, and preconditions. |
| Trying guessed space-list request names such as `GetAllSpacesRequest`, `GetSpaceListRequest`, or `QuerySpaceListRequest` | Use only `GetSpacesRequest` to list spaces. |
| Guessing or fabricating `AQARA_ENDPOINT_URL` or `AQARA_OPEN_API_TOKEN` | These must be read from the runtime environment. |
## Files
- `scripts/fetch_all_devices.sh` — cache refresh script; calls `GetAllDevicesWithSpaceRequest` and writes `data/devices.json`
- `data/devices.json` — cache file generated by the script; contains complete device data
- `references/examples.md` — example curl invocations
- `references/trait-codes.md` — full list of trait codes with type, unit, read/write, subscribe flags
Keep this `SKILL.md` lean; consult references for details.
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### scripts/fetch_all_devices.sh
```bash
#!/usr/bin/env bash
set -euo pipefail
# Fetch all devices with space info from Aqara Open API
# and write the response data array to data/devices.json.
#
# Usage:
# bash scripts/fetch_all_devices.sh
#
# Requires env vars:
# AQARA_ENDPOINT_URL — API base URL
# AQARA_OPEN_API_TOKEN — Bearer token (JWT)
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
SKILL_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
DATA_DIR="$SKILL_ROOT/data"
OUT_FILE="$DATA_DIR/devices.json"
if [ -z "${AQARA_ENDPOINT_URL:-}" ]; then
echo "ERROR: AQARA_ENDPOINT_URL is not set." >&2
exit 1
fi
if [ -z "${AQARA_OPEN_API_TOKEN:-}" ]; then
echo "ERROR: AQARA_OPEN_API_TOKEN is not set." >&2
exit 1
fi
mkdir -p "$DATA_DIR"
RESPONSE=$(curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"type\":\"GetAllDevicesWithSpaceRequest\",\"version\":\"v1\",\"msgId\":\"fetch-$(date +%s)\"}")
RESPONSE_CODE=$(echo "$RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin).get('code','-1'))" 2>/dev/null || echo "-1")
if [ "$RESPONSE_CODE" != "0" ]; then
echo "ERROR: API returned code=$RESPONSE_CODE" >&2
echo "$RESPONSE" >&2
exit 1
fi
echo "$RESPONSE" | python3 -c "
import sys, json
resp = json.load(sys.stdin)
data = resp.get('data', [])
print(json.dumps(data, ensure_ascii=False, indent=2))
" > "$OUT_FILE"
DEVICE_COUNT=$(python3 -c "import json; print(len(json.load(open('$OUT_FILE'))))" 2>/dev/null || echo "?")
echo "OK: wrote $DEVICE_COUNT devices to $OUT_FILE"
```
### references/trait-codes.md
```markdown
# 功能点代码一览
下表汇总了 Aqara 支持的全部功能点代码(trait code):
> 说明
>
> 下表仅展示各功能点代码(trait code)的基本信息,包括类型、可读写、可上报等。若需了解每个 trait 的详细定义(如取值范围、枚举说明等),请通过接口 [GetDevicesRequest](./trait-api.mdx#查询设备的-spec-配置) 获取完整的配置信息。
| trait_code | trait_name(chinese) | trait_name(English) | 功能类型 | 单位 | 可读 | 可写 | 可上报 |
|---|---|---|---|---|---|---|---|
| CODetected | 一氧化碳检测状态 | Carbon Monoxide Detection State | boolean | | ✔ | ✘ | ✔ |
| ExpressedState | 报警器状态 | Alarm Status | enum | | ✔ | ✘ | ✔ |
| WindSetting | 风类型 | Wind Type | enum | | ✔ | ✔ | ✔ |
| AirflowDirection | 风向 | Airflow Direction | enum | | ✔ | ✔ | ✔ |
| FanSpeed | 风速 | Fan Speed | number | % | ✔ | ✔ | ✔ |
| AirQuality | 空气质量等级 | Air Quality Level | enum | | ✔ | ✘ | ✔ |
| BooleanState | 布尔状态 | Boolean State | boolean | | ✔ | ✘ | ✔ |
| MatterVendorID | Matter Vendor ID | Matter Vendor ID | string | | ✔ | ✘ | ✔ |
| FreshAirMode | 新风模式 | Fresh Air Mode | enum | | ✔ | ✔ | ✔ |
| InvertedRotationAngle | 倒置旋转角度 | Inverted Rotation Angle | number | ° | ✘ | ✘ | ✔ |
| ConnectionStatus | 连接状态 | Connection Status | enum | | ✔ | ✘ | ✔ |
| ScreenBrightness | 屏幕亮度 | Screen Brightness | number | % | ✔ | ✔ | ✔ |
| TemperatureUIDisplayMode | 温度用户界面显示模式 | Temperature UI Display Mode | enum | | ✔ | ✔ | ✔ |
| BatReplacementNeeded | 是否需要更换电池 | Battery Replacement Needed | boolean | | ✔ | ✘ | ✔ |
| BatQuantity | 电池数量 | Battery Quantity | number | | ✔ | ✘ | ✔ |
| Rechargeable | 是否可充电 | Rechargeable | boolean | | ✔ | ✘ | ✔ |
| CircuitCurrent | 电路电流 | Circuit Current | number | A | ✔ | ✘ | ✔ |
| P2PCaptureEnabled | 是否支持P2P抓图 | P2P Capture Enabled | boolean | | ✔ | ✔ | ✔ |
| MotorOperationStatus | 电机运行状态 | Motor Operation Status | enum | | ✔ | ✘ | ✔ |
| MotorDirectionReversed | 电机方向反转 | Motor Direction Reversed | boolean | | ✔ | ✔ | ✔ |
| MotorControllability | 电机是否可控制 | Motor Controllable | boolean | | ✔ | ✘ | ✔ |
| CurrentValveState | 当前阀门状态 | Current Valve State | enum | | ✔ | ✘ | ✔ |
| Condition | 滤芯剩余寿命 | Filter Remaining Life | number | % | ✔ | ✘ | ✔ |
| ChangeIndication | 滤芯寿命状态 | Filter Life Status | enum | | ✔ | ✘ | ✔ |
| FilterType | 滤芯类型 | Filter Type | enum | | ✔ | ✘ | ✔ |
| OperationalState | 设备状态 | Device Status | enum | | ✔ | ✘ | ✔ |
| CurrentFlow | 当前流量 | Current Flow | number | m³/h | ✔ | ✘ | ✔ |
| CurrentMode | 当前模式 | Current Mode | enum | | ✔ | ✘ | ✔ |
| ChangeToModeResponse | 模式切换反馈 | Change Mode Response | enum | | ✔ | ✘ | ✔ |
| VacuumOperationalState | 扫地机器人状态 | Robot Vacuum State | enum | | ✔ | ✘ | ✔ |
| RefrigeratorAlarm | 冰箱报警 | Refrigerator Alarm | other | | ✔ | ✘ | ✔ |
| EvseState | 充电枪状态 | EVSE Gun State | enum | | ✔ | ✘ | ✔ |
| EvseSupplyState | 充电桩状态 | EVSE Supply State | enum | | ✔ | ✘ | ✔ |
| ChargingEnabledUntil | 允许继续充电时间 | Charging Allowed Until | number | A | ✔ | ✘ | ✔ |
| MaxChargeCurrent | 最大电流 | Max Charge Current | number | A | ✔ | ✘ | ✔ |
| BatteryCapacity | 电池容量 | Battery Capacity | number | | ✔ | ✘ | ✔ |
| MinChargeCurrent | 最小电流 | Min Charge Current | number | A | ✔ | ✘ | ✔ |
| SetTemperature | 设置温度 | Set Temperature | number | °C | ✔ | ✔ | ✔ |
| SetTemperatureLevel | 设置温度等级 | Set Temperature Level | number | | ✔ | ✔ | ✔ |
| MediaInput | 播放源 | Media Source | enum | | ✔ | ✔ | ✔ |
| SetBack | 温度浮动 | Temperature Offset | number | °C | ✔ | ✔ | ✔ |
| OpenAngle | 开合角度 | Opening Angle | number | | ✔ | ✘ | ✔ |
| BSSID | BSSID | BSSID | string | | ✔ | ✘ | ✔ |
| CurrentTemperatureCalibration | 温度补偿 | Temperature Calibration | number | °C | ✔ | ✔ | ✔ |
| HeaterCoolerButtonEvent | 温控按键事件 | Thermostat Button Event | enum | | ✔ | ✘ | ✔ |
| HeaterCoolerPolarity | 温控器极性设置 | Thermostat Polarity Setting | enum | | ✔ | ✔ | ✔ |
| SelfCheck | 报警器自检 | Alarm Self-Check | boolean | | ✔ | ✔ | ✔ |
| FirmwareRevision | 固件版本 | Firmware Version | string | | ✔ | ✘ | ✔ |
| SerialNumber | 序列号 | Serial Number | string | | ✔ | ✘ | ✔ |
| VendorName | 厂商名 | Vendor Name | string | | ✔ | ✘ | ✔ |
| VendorID | 厂商ID | Vendor ID | string | | ✔ | ✘ | ✔ |
| ProductName | 产品名称 | Product Name | string | | ✔ | ✘ | ✔ |
| Reachable | 连接状态 | Connection Status | boolean | | ✔ | ✘ | ✔ |
| HardwareVersion | 硬件版本 | Hardware Version | string | | ✔ | ✘ | ✔ |
| Mac | Mac地址 | MAC Address | string | | ✔ | ✘ | ✔ |
| DeviceID | 设备ID | Device ID | string | | ✔ | ✘ | ✔ |
| EndpointName | 设备或卡片名称 | Device or Card Name | string | | ✔ | ✔ | ✔ |
| OnOff | 开关状态 | On / Off State | boolean | | ✔ | ✔ | ✔ |
| CurrentLevel | 当前等级 | Current Level | number | % | ✔ | ✔ | ✔ |
| CurrentX | X值 | X Value | number | | ✔ | ✔ | ✔ |
| CurrentY | Y值 | Y Value | number | | ✔ | ✔ | ✔ |
| ColorTemperature | 色温值 | Color Temperature | number | | ✔ | ✔ | ✔ |
| ButtonEvent | 按钮事件 | Button Event | enum | | ✘ | ✘ | ✔ |
| CameraActiveStatus | 摄像头休眠状态 | Camera Sleep Status | boolean | | ✔ | ✔ | ✔ |
| CurrentPositionPercentage | 当前位置百分比 | Current Position Percentage | number | % | ✔ | ✘ | ✔ |
| TargetPositionPercentage | 目标位置百分比 | Target Position Percentage | number | % | ✘ | ✔ | ✔ |
| IRType | 红外类型 | IR Type | enum | | ✔ | ✔ | ✔ |
| IRBrand | 红外品牌 | IR Brand | number | | ✔ | ✔ | ✔ |
| CurrentVoltage | 电压值 | Voltage Value | number | V | ✔ | ✘ | ✔ |
| CurrentPower | 功率值 | Power Value | number | W | ✔ | ✘ | ✔ |
| CumulativeEnergyConsumption | 累计耗电量 | Cumulative Energy Consumption | number | W·h | ✔ | ✘ | ✔ |
| HeaterCoolerMode | 制热制冷模式 | Heat/Cool Mode | enum | | ✔ | ✔ | ✔ |
| HeatingTemperature | 制热温度 | Heating Temperature | number | °C | ✔ | ✔ | ✔ |
| CoolingTemperature | 制冷温度 | Cooling Temperature | number | °C | ✔ | ✔ | ✔ |
| FanMode | 风扇模式 | Fan Mode | enum | | ✔ | ✔ | ✔ |
| RockSetting | 摆风 | Swing Mode | enum | | ✔ | ✔ | ✔ |
| CurrentHumidity | 湿度 | Humidity | number | % | ✔ | ✘ | ✔ |
| CurrentPlaybackState | 播放状态 | Playback State | enum | | ✔ | ✘ | ✔ |
| PlaybackDuration | 时长 | Duration | number | ms | ✔ | ✘ | ✔ |
| SampledPosition | 播放进度 | Playback Progress | number | ms | ✔ | ✘ | ✔ |
| PlaybackMode | 播放模式 | Playback Mode | enum | | ✔ | ✔ | ✔ |
| MediaInformation | 媒体信息 | Media Information | string | | ✔ | ✘ | ✔ |
| Volume | 音量 | Volume | number | % | ✔ | ✔ | ✔ |
| Mute | 静音 | Mute | boolean | | ✔ | ✔ | ✔ |
| LockState | 锁状态 | Lock Status | enum | | ✔ | ✘ | ✔ |
| DoorState | 门状态 | Door Status | enum | | ✔ | ✘ | ✔ |
| IRKey | 红外指令 | IR Command | enum | | ✘ | ✔ | ✘ |
| BatPercentRemaining | 电量百分比 | Battery Percentage Remaining | number | % | ✔ | ✘ | ✔ |
| SweeperState | 扫地状态 | Sweeper State | enum | | ✔ | ✘ | ✔ |
| VOCDensity | VOC浓度 | VOC Concentration | number | ppb | ✔ | ✘ | ✔ |
| VOCQuality | VOC等级 | VOC Level | enum | | ✔ | ✘ | ✔ |
| CurrentPressure | 压力 | Pressure | number | kPa | ✔ | ✘ | ✔ |
| PM2.5Density | PM2.5浓度 | PM2.5 Concentration | number | ug/m³ | ✔ | ✘ | ✔ |
| PM2.5Level | PM2.5等级 | PM2.5 Level | enum | | ✔ | ✘ | ✔ |
| PM1.0Density | PM1.0浓度 | PM1.0 Concentration | number | ug/m³ | ✔ | ✘ | ✔ |
| PM10Density | PM10浓度 | PM10 Concentration | number | ug/m³ | ✔ | ✘ | ✔ |
| CO2Density | CO2浓度 | CO2 Concentration | number | ppm | ✔ | ✘ | ✔ |
| CO2Level | CO2等级 | CO2 Level | enum | | ✔ | ✘ | ✔ |
| CurrentIlluminance | 光照度 | Illuminance | number | lux | ✔ | ✘ | ✔ |
| ContactSensorState | 接触状态 | Contact State | boolean | | ✔ | ✘ | ✔ |
| SmokeDensity | 烟雾浓度 | Smoke Concentration | number | OBS%/FT | ✔ | ✘ | ✔ |
| SmokeDetected | 烟雾检测状态 | Smoke Detection State | boolean | | ✔ | ✘ | ✔ |
| GasDensity | 气体浓度 | Gas Concentration | number | %LEL | ✔ | ✘ | ✔ |
| GasDetected | 气体检测状态 | Gas Detection State | boolean | | ✔ | ✘ | ✔ |
| SleepState | 睡眠状态 | Sleep State | enum | | ✔ | ✘ | ✔ |
| SleepQuality | 睡眠质量 | Sleep Quality | number | | ✔ | ✘ | ✔ |
| OnBed | 在床状态 | On Bed State | boolean | | ✔ | ✘ | ✔ |
| SleepStage | 睡眠阶段 | Sleep Stage | enum | | ✔ | ✘ | ✔ |
| LeakState | 泄漏状态 | Leak State | boolean | | ✔ | ✘ | ✔ |
| Occupancy | 存在状态 | Occupancy State | boolean | | ✔ | ✘ | ✔ |
| OccupancySensorType | 存在传感类型 | Occupancy Sensor Type | enum | | ✔ | ✘ | ✔ |
| MotionDetected | 移动侦测 | Motion Detection | enum | | ✘ | ✘ | ✔ |
| RotationAngle | 旋转角度 | Rotation Angle | number | | ✘ | ✘ | ✔ |
| RotationDirection | 旋转方向 | Rotation Direction | enum | | ✘ | ✘ | ✔ |
| RotationEvent | 旋转事件 | Rotation Event | enum | | ✘ | ✘ | ✔ |
| CubeEvent | 魔方事件 | Cube Event | enum | | ✘ | ✘ | ✔ |
| VibrationEvent | 动静贴事件 | Vibration Event | enum | | ✘ | ✘ | ✔ |
| SmokeDensitydB | 烟雾浓度dB | Smoke Concentration (dB) | number | dB/m | ✔ | ✘ | ✔ |
| CurrentR | 红色值 | Red Value | number | | ✔ | ✔ | ✔ |
| CurrentG | 绿色值 | Green Value | number | | ✔ | ✔ | ✔ |
| CurrentB | 蓝色值 | Blue Value | number | | ✔ | ✔ | ✔ |
| EndpointArrayDynamic | 节点动态数组 | Dynamic Endpoint Array | other | | ✔ | ✘ | ✔ |
| Feed | 喂食 | Feeding | enum | | ✔ | ✘ | ✔ |
| AttitudeDetected | 姿态检测 | Posture Detection | enum | | ✔ | ✘ | ✔ |
| TargetPlaybackState | 目标播放状态 | Target Playback State | enum | | ✔ | ✔ | ✔ |
| EnableRemoteControl | 远程控制使能 | Remote Control Enabled | boolean | | ✔ | ✔ | ✔ |
| HardwareVersionString | 硬件版本号(字符串类型) | Hardware Version (string) | string | | ✔ | ✘ | ✔ |
| FirmwareRevisionString | 固件版本号(字符串类型) | Firmware Version (string) | string | | ✔ | ✘ | ✔ |
| ProductID | 产品ID | Product ID | number | | ✔ | ✘ | ✔ |
| MinPINCodeLength | 最小PINCode长度 | Minimum PINCode Length | number | | ✔ | ✘ | ✔ |
| MinRFIDCodeLength | 最小RFID长度 | Minimum RFID Length | number | | ✔ | ✘ | ✔ |
| OperatingMode | 操作模式 | Operating Mode | enum | | ✔ | ✘ | ✔ |
| ActuatorEnabled | 执行器启用 | Actuator Enabled | boolean | | ✔ | ✘ | ✔ |
| ReverseIdentify | 反向识别 | Reverse Identify | number | | ✘ | ✘ | ✔ |
| Hue | Hue | Hue | number | ° | ✔ | ✔ | ✔ |
| Saturation | 饱和度 | Saturation | number | % | ✔ | ✔ | ✔ |
| MinHeatCoolDeadBand | 制冷制热温度最小死区 | Min Heat/Cool Deadband | number | °C | ✔ | ✔ | ✔ |
| Channel | 无线信道 | Wireless Channel | number | | ✔ | ✘ | ✔ |
| ExtendedPANID | 网络ExtendedPANID | Network ExtendedPANID | number | | ✔ | ✘ | ✔ |
| WindowCoveringMotorsBinding | 窗帘电机绑定为统一总控 | Curtain Motors Unified Control | boolean | | ✔ | ✔ | ✔ |
| CurrentRotationAngle | 窗帘的当前旋转角度 | Current Curtain Rotation Angle | number | ° | ✔ | ✘ | ✔ |
| TargetRotationAngle | 窗帘的目标旋转角度 | Target Curtain Rotation Angle | number | ° | ✔ | ✔ | ✔ |
| CurrentGarageDoorState | 当前(车库)门状态 | Current Garage Door State | enum | | ✔ | ✘ | ✔ |
| TargetGarageDoorState | 目标(车库)门状态 | Target Garage Door State | enum | | ✔ | ✔ | ✔ |
| HoldRotationAngle | 按住旋转角度 | Hold Rotation Angle | number | ° | ✘ | ✘ | ✔ |
| ModeCount | 模式数量 | Mode Count | number | | ✔ | ✘ | ✔ |
| PlaybackSampledPosition | 当前媒体播放位置 | Current Media Playback Position | number | ms | ✔ | ✔ | ✔ |
| SelectedMediaOutputSource | 当前播放输出源 | Current Media Output Source | enum | | ✔ | ✔ | ✔ |
| LockOperation | 门锁操作(事件)上报 | Lock Operation (Event) Report | other | | ✘ | ✘ | ✔ |
| DoorLockAlarm | 门锁报警(事件)上报 | Door Lock Alarm (Event) Report | other | | ✘ | ✘ | ✔ |
| LockUserChange | 门锁用户变更上报 | Lock User Change Report | other | | ✘ | ✘ | ✔ |
| ModelValue | 模型值 | Model Value | string | | ✔ | ✘ | ✔ |
```
### references/examples.md
```markdown
# Aqara Open API — curl Examples
All examples assume environment variables are set:
```bash
export AQARA_OPEN_API_TOKEN="your-bearer-token"
export AQARA_ENDPOINT_URL="https://aiot-open-3rd.aqara.cn/open/api"
```
---
## Device Management
### Load All Devices (with space info, full detail — cached to file)
This is the primary data source. Run the cache script once; subsequent device, space, and status queries read from the local file `data/devices.json`. Only re-run when the cache is missing or the user explicitly asks to refresh.
```bash
# Preferred: use the cache script (fetches API + writes data/devices.json)
bash scripts/fetch_all_devices.sh
# Manual curl (for reference only — prefer the script):
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"GetAllDevicesWithSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'"}'
```
`data/devices.json` contains all devices with full `endpoints[]`, current trait values, and `space` info. Filter locally by `deviceTypesList`, `name`, `state`, and `space.name`.
### Get All Device Types
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"GetDeviceTypeInfosRequest","version":"v1","msgId":"msg-'$(date +%s)'"}'
```
### Device Status / Trait Values
**No separate API call needed.** Current trait values are included in `data/devices.json` under each device's `endpoints[].functions[].traits[].value`.
### Control Device — Turn On
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"ExecuteTraitRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":[{"deviceId":"lumi.device.abc123","endpointId":1,"functionCode":"Output","traitCode":"OnOff","value":true}]}'
```
### Control Device — Turn Off
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"ExecuteTraitRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":[{"deviceId":"lumi.device.abc123","endpointId":1,"functionCode":"Output","traitCode":"OnOff","value":false}]}'
```
### Control Device — Set Brightness
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"ExecuteTraitRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":[{"deviceId":"lumi.device.abc123","endpointId":1,"functionCode":"LevelControl","traitCode":"CurrentLevel","value":80}]}'
```
---
## Space Management
### List Spaces
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"GetSpacesRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":null}'
```
### Create a Top-Level Space
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"CreateSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"name":"My Home"}}'
```
### Create a Sub-Space
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"CreateSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"name":"Living Room","parentSpaceId":"space_id_123","spatialMarking":"living_room"}}'
```
### Update Space
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"UpdateSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"spaceId":"space_id_123","name":"New Room Name"}}'
```
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"UpdateSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"spaceId":"space_id_123","spatialMarking":"bedroom"}}'
```
### Assign Devices to Space
```bash
curl -s -X POST "$AQARA_ENDPOINT_URL" \
-H "Authorization: Bearer $AQARA_OPEN_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"type":"AssociateDevicesToSpaceRequest","version":"v1","msgId":"msg-'$(date +%s)'","data":{"spaceId":"space_id_123","deviceIds":["lumi.device.abc123","lumi.device.def456"]}}'
```
---
```
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### _meta.json
```json
{
"owner": "aiot-f2e-team",
"slug": "aqara-open-api",
"displayName": "Aqara OpenAPI",
"latest": {
"version": "1.0.2",
"publishedAt": 1773109547553,
"commit": "https://github.com/openclaw/skills/commit/1719ffaa4d8a14777d60e8ef5fb9020d992b0b32"
},
"history": []
}
```