原始内容
name: recamera-intellisense description: Register and control reCamera Pro devices from an agent — onboard cameras, pick AI detection models by name, configure rule-based triggers (AI / timer / GPIO / TTY / HTTP / always-on / sound-event), poll detection events with snapshots, capture JPG/RAW/MP4 on demand, browse recorded clips, manage storage, and drive GPIO pins. Uses a bundled stdlib-only Python SDK invoked via a single JSON argument per command. Trigger this skill whenever the user mentions reCamera, camera onboarding, object/person detection, sound event detection, event polling, snapshot or video capture, recording rules, on-device GPIO, or asks to wire a physical camera into an agent workflow — even when they don't name the product explicitly. metadata: { "openclaw": { "emoji": "📷", "requires": { "bins": ["python3"], "config_paths": ["~/.recamera/devices.json"] } } }
user-invocable: true
reCamera Intellisense
Drive one or more reCamera Pro devices: device registration, AI detection configuration, sound-event detection, rule-based recording, event polling, on-demand capture, storage/records management, and GPIO control. The skill bundles a Python SDK (scripts/recamera_intellisense/) that is the single source of truth for every command's parameters; the same package backs the MCP server, so CLI and MCP schemas are identical.
Requirements
python3≥ 3.9 (stdlib only — nopip installneeded)- Reachable reCamera HTTP/HTTPS API (default TCP
80/443) - Per-device auth token (Web Console → Device Info → Connection Settings → HTTP/HTTPS). Local devices may not require a token — pass an empty string. Tokens are arbitrary non-whitespace strings.
- Credential store at
~/.recamera/devices.json(auto-created, chmod600). Shared with the MCP server if installed.
Security
- Tokens are long-lived bearer credentials — do not commit
~/.recamera/devices.json(auto-createdchmod 600), and avoid logging thetokenfield. - Same-origin redirect enforcement — the HTTP client refuses to follow any 3xx redirect whose
(scheme, host, port)differs from the registered device, so the bearer token is never forwarded to an unexpected origin (SSRF-safe). - Secure-by-default TLS — HTTPS devices validate the certificate chain unless you explicitly register with
"allow_unsecured": true(intended for self-signed LAN certs; do not use on the public Internet). Local HTTPS devices always use self-signed certificates, so register them with"protocol":"https"and"allow_unsecured":true. - HTTP is the default transport. On untrusted networks, provision HTTPS on the device and register with
"protocol":"https"plus a trusted cert. - Path hygiene —
fetch_file/delete_filereject non-absolute paths,..traversal segments, and NUL bytes client-side; the daemon additionally enforces an allowlist. - Scope of trust: this skill reads and writes files on the camera (captures, events), controls GPIO, and can format storage. Only point it at hardware you own.
- Source review encouraged — the full SDK is under
scripts/recamera_intellisense/; every command is a short, stdlib-only Python function.
Invocation
The bundled SDK runs without installation. From {baseDir} (the skill root):
# One-shot (recommended)
PYTHONPATH="{baseDir}/scripts" python3 -m recamera_intellisense <command> '<json>'
# Direct module execution also works
PYTHONPATH="{baseDir}/scripts" python3 {baseDir}/scripts/recamera_intellisense/device.py <command> '<json>'
# Convenience alias for a session
export PYTHONPATH="{baseDir}/scripts"
alias rci='python3 -m recamera_intellisense'
rci list_devices
Calling convention (uniform across every command):
- Input: exactly one CLI positional argument — a JSON object whose keys match the function's keyword parameters. Omit the argument for commands with no required fields (e.g.
list_devices). - Success: pretty-printed JSON on stdout (mutating commands may print nothing); exit code
0. - Failure: actionable message on stderr; non-zero exit code. Surface the stderr back to the user and propose one concrete fix.
- Discovery:
python3 -m recamera_intellisensewith no args prints every command with its required/optional keys.
Python use (in-process, preferred for loops):
import sys, time
sys.path.insert(0, "{baseDir}/scripts")
from recamera_intellisense import (
list_devices, set_detection_rules, get_detection_events,
capture_image, fetch_file,
)
Command catalogue
All commands accept device_name (string) unless noted. device_name resolves against ~/.recamera/devices.json; run list_devices to see what is registered. Selector commands (get_device, remove_device, everything that takes device_name) also accept the alias name, so a record emitted by list_devices can be forwarded verbatim.
Device registry
| Command | Required | Optional |
|---|---|---|
detect_local_device |
host |
port, token (empty for devices that do not require auth), timeout |
add_device |
name, host, token (empty for local devices) |
protocol (http/https), allow_unsecured, port |
update_device |
device_name |
any of host, token (empty for local devices), protocol, allow_unsecured, port |
remove_device / get_device |
device_name |
— |
list_devices |
— | — |
add_device / update_device probe /api/v1/recamera-generate-204 before persisting; a bad token or host fails fast.
Detection (high-level facade, AI-only)
| Command | Required | Optional |
|---|---|---|
get_detection_models_info |
device_name |
— |
get_detection_model |
device_name |
— |
set_detection_model |
device_name |
one of model_id or model_name, fps (default 30) |
get_detection_schedule / set_detection_schedule |
device_name |
schedule (null / empty list / omit the key disables → always active) |
get_detection_rules |
device_name |
— |
set_detection_rules |
device_name, rules |
ensure_writer (default true), ensure_storage (default true) |
get_detection_events |
device_name |
start_unix_ms, end_unix_ms (inclusive) |
clear_detection_events |
device_name |
— |
Acoustic (sound-event detection)
| Command | Required | Optional |
|---|---|---|
get_active_acoustic_model |
device_name |
— |
Use this to discover labels for the sed record trigger.
set_detection_rules installs an inference_set record trigger and, by default, enables the rule pipeline with a JPG writer and a ready storage slot — override only if the caller already configured them. get_detection_rules returns [] whenever the current trigger is not inference_set.
Rule system (low-level, all trigger kinds)
get_rule_system_info, get_record_config, set_record_config, get_schedule_rule, set_schedule_rule, get_record_trigger, set_record_trigger, activate_http_trigger. Use these to combine detection with non-AI triggers (see below) or to tune the writer. Trigger kinds include inference_set, timer, gpio, tty, http, always_on, and sed (sound-event detection).
Capture (independent of rule pipeline)
| Command | Required | Optional |
|---|---|---|
get_capture_status |
device_name |
— |
start_capture |
device_name |
output (absolute on-device directory; omit to use the selected storage slot), format (JPG/RAW/MP4), video_length_seconds |
stop_capture |
device_name |
— |
capture_image |
device_name |
output, timeout |
capture_image is the one-shot helper: it starts a JPG, polls until terminal, fetches the file, and returns {event, path, size, content_base64} (where event is the terminal capture event with status == "COMPLETED"). For MP4, drive the pipeline manually: start_capture → poll get_capture_status until last_capture.status is terminal (COMPLETED / FAILED / INTERRUPTED / CANCELED) → fetch_file using the event's output_directory + file_name. stop_capture is only meaningful for in-flight video.
Storage
get_storage_status, set_storage_slot, configure_storage_quota, storage_task_submit, storage_task_status, storage_task_cancel. Actions: FORMAT, FREE_UP, EJECT, REMOVE_FILES_OR_DIRECTORIES (the last requires files, paths relative to the slot's data directory). Default is async; "sync": true is accepted only for fast actions (EJECT, REMOVE_FILES_OR_DIRECTORIES) — FORMAT/FREE_UP must run async and be polled with storage_task_status.
Records (relay-backed browsing, recommended for recordings)
list_records {device_name, path?, dev_path?, limit?, offset?}—pathis relative to the record data directory (empty = root). Returns a paginated object{entries, offset, limit, total, has_more}; directories sort first, then by name.limitdefaults to100, max500. Slot auto-resolved ifdev_pathomitted.fetch_record {device_name, path, dev_path?, max_inline_bytes?}— images/≤5 MiB inline as base64; videos or larger payloads return{url, size, note}.
Files (daemon, arbitrary absolute paths)
fetch_file {device_name, path, max_inline_bytes?}— for detection-event snapshots (snapshot_pathis absolute) and arbitrary allowed paths. Returns{path, content_type, size, content_base64}inline, or{path, content_type, size, note}when oversized. (Python callers may also passraw=Trueto get rawbytes; the CLI does not acceptraw.)delete_file {device_name, path}.
GPIO
list_gpios, get_gpio_info {pin_id}. set_gpio_value {pin_id, value} drives 0 or 1 and auto-reconfigures the pin as push-pull output. get_gpio_value {pin_id, debounce_ms?} returns 0 or 1 and auto-reconfigures the pin as floating input. Debounce defaults to 100 ms; when debounce_ms > 0 and the pin's edge mode was none, the SDK also switches it to both-edge detection (the device rejects debounce with edge=none). Both calls have this side effect on pin direction — don't use them as read-only probes.
Key schemas
Detection rule
{
"name": "front-door-person",
"debounce_times": 3,
"confidence_range_filter": [0.25, 1.0],
"label_filter": ["person"],
"region_filter": [[[0.1,0.1],[0.9,0.1],[0.9,0.9],[0.1,0.9]]]
}
label_filtercontains label names as they appear inget_detection_models_info.labels (vision) orget_active_acoustic_model.labels (sound) — never indexes. Leave empty to match any label.region_filteris a list of polygons of normalized[x, y]in[0,1]; omit or leave null for the full frame.confidence_range_filteris[min, max]with both in[0.0, 1.0]andmin ≤ max; defaults to[0.25, 1.0].debounce_timesdefaults to3(consecutive matching frames).
Schedule range
{"start": "Mon 08:00:00", "end": "Mon 18:00:00"} — three-letter day (Mon/Tue/Wed/Thu/Fri/Sat/Sun); Day 24:00:00 is valid. Pass a list; null or [] disables and means "always active".
Record trigger (tagged union, kind field)
{"kind":"inference_set", "rules":[ /* DetectionRule[] */ ]}
{"kind":"timer", "interval_seconds": 60}
{"kind":"gpio", "num":1, "state":"PULL_UP", "signal":"FALLING", "debounce_ms":50}
{"kind":"tty", "name":"tty0", "command":"SHOOT"}
{"kind":"http"}
{"kind":"always_on"}
{"kind":"sed", "model_id":"", "consecutive_window_ms":0, "confidence_range_filter":[0.5,1.0], "label_filter":["Cat"]}
For gpio provide one of name or num; state ∈ {DISABLED,FLOATING,PULL_UP,PULL_DOWN}; signal ∈ {HIGH,LOW,RISING,FALLING}. For sed, model_id is the acoustic runtime_head_id; leave it empty to accept the currently active model, and use labels from get_active_acoustic_model.
Detection event
{"timestamp":"2026-04-20T12:34:56Z","timestamp_unix_ms":1745152496000,"rule_name":"front-door-person","snapshot_path":"/mnt/.../abcd.jpg"}
snapshot_path (when present) is an absolute on-device path — feed it to fetch_file, not fetch_record.
Agent rules
- Always supply a complete JSON object; never prompt interactively.
- Identify the target by
device_name(preferred).list_devicesis cheap — call it if unsure. - Token is a string without whitespace; it may be empty for local/trusted devices that do not require auth.
label_filtertakes label names fromget_detection_models_info(vision) orget_active_acoustic_model(sound). Do not translate names into numeric indexes — the MCP/SDK expects strings.- For AI-only use, prefer
set_detection_rules(it ensures writer + storage). For hybrid triggers (GPIO, timer, TTY, HTTP, always-on, SED), useset_record_triggerdirectly — only one record trigger is active at a time. - Poll
get_detection_eventswith a checkpointedstart_unix_ms(1–10 s cadence). Events accumulate on-device;clear_detection_eventsto reset. - Prefer event metadata first; fetch imagery only when the user needs it. Images/≤5 MiB inline; videos/larger return a URL +
note. - When a storage slot is required (rules, timer, always-on), the facade's
ensure_storage=truedefault will select the internal slot if none is enabled; for removable media the user must provision a slot beforehand.
Workflows
1 — Onboard a device
- (Optional)
detect_local_device '{"host":"192.168.1.100"}'→ returns{detected, host, port, protocol, allow_unsecured}. add_device '{"name":"cam1","host":"192.168.1.100","token":"sk_xxxx","protocol":"https","allow_unsecured":true}'(copyprotocol/allow_unsecuredfrom the detection result). Local HTTP device:add_device '{"name":"local","host":"127.0.0.1","token":""}'.list_devicesto confirm.
2 — Person/object detection by name
get_detection_models_info→ inspectlabelsto choose a target label.set_detection_model '{"device_name":"cam1","model_name":"yolo11n"}'.set_detection_rules '{"device_name":"cam1","rules":[{"name":"front-door-person","label_filter":["person"],"debounce_times":3}]}'.- (Optional)
set_detection_schedulefor office hours. clear_detection_eventsto start fresh.
3 — Monitor events (long-running)
ckpt = int(time.time() * 1000)
while True:
events = get_detection_events(device_name="cam1", start_unix_ms=ckpt)
for e in events:
ckpt = max(ckpt, e["timestamp_unix_ms"] + 1)
if e.get("snapshot_path"):
img = fetch_file(device_name="cam1", path=e["snapshot_path"])
# img["content_base64"] or dispatch on img["url"] when oversized
time.sleep(2)
4 — On-demand snapshot / video
- JPG:
capture_image '{"device_name":"cam1"}'→ returns base64 inline. - MP4:
start_capture '{"device_name":"cam1","format":"MP4","video_length_seconds":10}', then pollget_capture_statusuntillast_capture.statusis terminal, thenfetch_filewith the absolute path assembled from the event'soutput_directory+file_name.
5 — Browse and retrieve recordings
list_records '{"device_name":"cam1"}'→{entries, offset, limit, total, has_more}; iterate top-level date folders fromentries.list_records '{"device_name":"cam1","path":"2026-04-20","limit":200,"offset":0}'— drill in, paginate ifhas_moreis true.fetch_record '{"device_name":"cam1","path":"2026-04-20/clip-001.mp4"}'→ returns{url, note}for video.
6 — Hybrid trigger (GPIO pulse → 5 s MP4)
{"device_name":"cam1","trigger":{"kind":"gpio","num":1,"state":"PULL_UP","signal":"FALLING","debounce_ms":50}}
Pair with set_record_config '{"device_name":"cam1","rule_enabled":true,"writer_format":"MP4","writer_interval_ms":0}' and ensure a storage slot is selected (set_storage_slot).
7 — GPIO control
- Read:
get_gpio_value '{"device_name":"cam1","pin_id":2,"debounce_ms":50}'→ prints0or1. - Write:
set_gpio_value '{"device_name":"cam1","pin_id":1,"value":1}'→ prints1.
8 — Sound-event detection trigger
get_active_acoustic_model '{"device_name":"cam1"}'→ inspectlabels(e.g.["Cat","_background_noise_"]).- Configure the rule pipeline for the desired writer format, e.g.
set_record_config '{"device_name":"cam1","rule_enabled":true,"writer_format":"MP4","writer_interval_ms":5000}'. set_record_trigger '{"device_name":"cam1","trigger":{"kind":"sed","model_id":"","consecutive_window_ms":0,"confidence_range_filter":[0.5,1.0],"label_filter":["Cat"]}}'.- Ensure a storage slot is selected (
get_storage_status/set_storage_slot). - Poll
get_detection_eventsfor clips triggered by the target sound class.
CLI quickstart
export PYTHONPATH="{baseDir}/scripts"
alias rci='python3 -m recamera_intellisense'
rci # list all commands
rci add_device '{"name":"cam1","host":"192.168.1.100","token":"sk_xxxx"}'
# Local HTTPS device with self-signed cert and no token
rci add_device '{"name":"local","host":"127.0.0.1","token":"","protocol":"https","allow_unsecured":true}'
rci list_devices
rci get_detection_models_info '{"device_name":"cam1"}'
rci set_detection_model '{"device_name":"cam1","model_name":"yolo11n"}'
rci set_detection_rules '{"device_name":"cam1","rules":[{"name":"person","label_filter":["person"]}]}'
rci get_active_acoustic_model '{"device_name":"cam1"}'
rci set_record_trigger '{"device_name":"cam1","trigger":{"kind":"sed","model_id":"","consecutive_window_ms":0,"confidence_range_filter":[0.5,1.0],"label_filter":["Cat"]}}'
rci get_detection_events '{"device_name":"cam1","start_unix_ms":1745150000000}'
rci capture_image '{"device_name":"cam1"}'
rci list_records '{"device_name":"cam1"}'
rci fetch_record '{"device_name":"cam1","path":"2026-04-20/evt-001.jpg"}'
rci get_storage_status '{"device_name":"cam1"}'
rci set_gpio_value '{"device_name":"cam1","pin_id":1,"value":1}'
Execution checklist (copy for multi-step tasks)
reCamera Task Progress
- [ ] Resolve device (list_devices, then device_name)
- [ ] Validate JSON (required keys per command)
- [ ] Ensure prerequisites (storage slot, schedule, model) when configuring rules
- [ ] Run command; parse stdout or handle stderr
- [ ] For polling, checkpoint start_unix_ms and rate-limit
- [ ] On error, surface stderr + one concrete fix
Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
HTTP 401/403 |
Token missing/invalid — re-copy from Web Console |
Connection refused / timed out |
Wrong host/port/protocol; verify LAN reachability and device power. |
| HTTPS certificate error | Add "allow_unsecured": true for self-signed LAN certs (not for the public Internet). Local HTTPS devices always use self-signed certs, so this is required. |
add_device rejects empty token |
Empty tokens are only valid for local/trusted devices. For remote devices, copy the token from the Web Console. |
get_detection_rules returns [] |
Current trigger is not inference_set — call set_detection_rules (or inspect get_record_trigger). |
| Rules set but no events | No storage slot configured (get_storage_status); schedule window inactive; region_filter/confidence_range_filter too tight; debounce_times too high. |
fetch_record returns only {url, note} |
File is a video or exceeds max_inline_bytes (default 5 MiB). Fetch the URL directly or raise the budget. |
storage_task_submit rejects sync=true |
FORMAT/FREE_UP must run async — resubmit with "sync": false and poll storage_task_status. |
Schedule rejected |
Use Day HH:MM:SS with three-letter day; Day 24:00:00 valid. |
set_detection_model fails: "not installed" |
Run get_detection_models_info and use one of the returned names/ids. |
get_active_acoustic_model returns null |
No sound-event model is active. Open /extension/acousticslab on the device and activate a model. |
set_record_trigger (SED) fails |
Ensure a model is active (get_active_acoustic_model); verify label_filter uses returned label names and consecutive_window_ms is within [0, 60000]. |
detect_local_device returns {"detected":false} |
No reCamera API found on the tried ports. Verify the device is powered on, on the same network, and that its HTTP/HTTPS API is enabled. Pass port if the device uses a non-standard port. |
start_capture rejects output with code 30022 |
output must be an on-device directory under a mounted storage slot (check get_storage_status.mount_path). Local paths like /tmp/... are not valid — omit output to use the default slot, then fetch_file the result. |
ImportError: recamera_intellisense |
Set PYTHONPATH="{baseDir}/scripts" or cd {baseDir}/scripts before python3 -m recamera_intellisense. |
ImportError: attempted relative import with no known parent package |
PYTHONPATH is wrong or you ran the file from outside the package. Set PYTHONPATH="{baseDir}/scripts" and use python3 -m recamera_intellisense <command>, or run any module directly with PYTHONPATH set. |
Reference pointers
- Command schemas at runtime:
python3 -m recamera_intellisense(prints every command with required/optional keys). - Per-module sources:
scripts/recamera_intellisense/{device,detection,model,acoustic,rule,storage,records,capture,files,gpio,relay}.py— short, stdlib-only, each file'sCOMMAND_SCHEMASdict is authoritative. - Credential store:
~/.recamera/devices.json(schema matches the RustDeviceEntryin the MCP server; the two surfaces interoperate).