aqara-open-api — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited aqara-open-api (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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:
The following environment variables are required:
AQARA_ENDPOINT_URL: The base URLAQARA_OPEN_API_TOKEN: Your Long-Lived Access Token.This section is a navigation and execution summary only. It does not add new rules or change existing constraints.
deviceId, endpointId, functionCode, and traitCode from cachedata/devices.jsonbash scripts/fetch_all_devices.shdata/devices.json existsdeviceId + endpointId + functionCode + traitCode from cachecurl for ExecuteTraitRequestcurl for GetDeviceTypeInfosRequestbash scripts/fetch_all_devices.shcurl for GetSpacesRequestspaceId from GetSpacesRequestCreateSpaceRequest or UpdateSpaceRequestspaceId from GetSpacesRequestdeviceId values from data/devices.jsonAssociateDevicesToSpaceRequestGetAllDevicesWithSpaceRequest may only be executed via bash scripts/fetch_all_devices.sh.curl.type, version, msgId, and data; `version` must always be `"v1"`.GetAllSpacesRequest, GetSpaceListRequest, or QuerySpaceListRequest.deviceId, endpointId, functionCode, traitCode, or spaceId.data/devices.json existsdeviceId, endpointId, functionCode, and traitCode from the cache filespaceId is needed, call GetSpacesRequest firstspaceId from GetSpacesRequest and deviceId values from data/devices.jsondata/devices.json, which stores the data array from GetAllDevicesWithSpaceRequestendpoints[].endpointIdGetSpacesRequest or cached space.spaceId## API Protocol### Execution Model### File Cache Model (Cache-First Data Model)## API Commands under ### Device Management### Space Management## Standard Operating Procedures (SOP)## Cache Decision Tree and ## API Call Decision Tree## Forbidden Behaviorreferences/trait-codes.mdRole: You are a strict hardware interface controller. Never infer or guess IDs or capability fields.
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%".
AQARA_OPEN_API_TOKEN: your Aqara Open API Bearer token (JWT)AQARA_ENDPOINT_URL: the API base URLReal 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.
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"}'Base URL: $AQARA_ENDPOINT_URL
All requests use a single unified POST endpoint. Routing is determined by the type field in the JSON body.
The request-body JSON may contain exactly these 4 fields. Do not add, remove, or replace fields:
{
"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
"version":"v1".senderId, source, timestamp, or any field not defined in this document.data may contain made-up fields or undocumented shapes.GetSpacesRequest, then type must be exactly GetSpacesRequest.GetAllSpacesRequest, GetSpaceListRequest, QuerySpaceListRequest, ListSpacesRequest, or other self-created variants are forbidden unless they are explicitly documented in this file.Authorization: Bearer $AQARA_OPEN_API_TOKEN
Content-Type: application/json{
"type": "<ResponseType>",
"version": "v1",
"msgId": "<matching-id>",
"code": 0,
"message": null,
"data": { ... }
}code: 0 = success. Non-zero = error. Common error codes:
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:
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.
Execution rules (must follow):
data to data/devices.json. Do not call this request directly with curl.ExecuteTraitRequest, GetSpacesRequest, CreateSpaceRequest, UpdateSpaceRequest, and AssociateDevicesToSpaceRequest): execute them with bash + `curl`. The JSON body must contain exactly 4 keys: type, version, msgId, and data.type, payload shape, token, IDs, cache, and required preconditions, then retry the same documented request only if appropriate.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 valuesRefresh cache (create/overwrite cache file)
bash scripts/fetch_all_devices.shUsage rules (must follow)
data/devices.json exists and is non-empty.GetAllDevicesWithSpaceRequest again.bash scripts/fetch_all_devices.sh to refresh the cache. After refresh, re-read the cache file before continuing.bash scripts/fetch_all_devices.sh and then re-read the file.ExecuteTraitRequest) still go directly to the API via curl, but all deviceId / endpointId / functionCode / traitCode values must come from the cache file.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.When the user asks for a subset of devices (for example, all lights, devices in the bedroom, or online switches):
deviceTypesList, name, state, or space.name / space.spatialMarking.deviceTypesList.bash scripts/fetch_all_devices.sh to refresh cache and retry.data/devices.json and run bash scripts/fetch_all_devices.sh to regenerate.#### 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 scripts/fetch_all_devices.shManual 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)'"}'#### 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.
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.
# 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}]}'#### 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.
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.
# 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.
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.
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>"]}}'data/devices.json exists and is non-empty.bash scripts/fetch_all_devices.sh to generate the cache, then read the file.data/devices.json is missing, run bash scripts/fetch_all_devices.sh first.value.data/devices.json is missing, run bash scripts/fetch_all_devices.sh first.deviceId, endpointId, functionCode, and traitCode from the cache file.ExecuteTraitRequest curl command with only cached values.data only contains failed items; an empty array means all commands succeeded.data/devices.json.GetSpacesRequest curl command to retrieve the full space hierarchy tree.CreateSpaceRequest, UpdateSpaceRequest, or AssociateDevicesToSpaceRequest as needed.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 itUser 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 | 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. |
scripts/fetch_all_devices.sh — cache refresh script; calls GetAllDevicesWithSpaceRequest and writes data/devices.jsondata/devices.json — cache file generated by the script; contains complete device datareferences/examples.md — example curl invocationsreferences/trait-codes.md — full list of trait codes with type, unit, read/write, subscribe flagsKeep this SKILL.md lean; consult references for details.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.