Command-line interface for Axis cameras — configure AXIS Object Analytics, stream real-time events, manage ACAP apps, and run operations across camera fleets.
axctl aoa list 192.168.1.33
axctl events stream 192.168.1.33
axctl fleet ping lab
| Platform | Command |
|---|---|
| macOS Apple Silicon | curl -L .../axctl-macos-arm64.tar.gz | tar -xz && sudo mv axctl-macos-arm64 /usr/local/bin/axctl |
| macOS Intel | curl -L .../axctl-macos-x64.tar.gz | tar -xz && sudo mv axctl-macos-x64 /usr/local/bin/axctl |
| Linux x64 | curl -L .../axctl-linux-x64.tar.gz | tar -xz && sudo mv axctl-linux-x64 /usr/local/bin/axctl |
| Linux ARM64 | curl -L .../axctl-linux-arm64.tar.gz | tar -xz && sudo mv axctl-linux-arm64 /usr/local/bin/axctl |
| Windows x64 | Download axctl-windows-x64.zip from releases, extract, add to PATH |
See INSTALL.md for full instructions and SHA-256 checksums.
Requires Bun ≥ 1.1.
git clone https://github.com/oneshot2001/axctl.git && cd axctl
bun install
bun run build # native binary → ./axctl
bun run build:all # all 5 platforms → dist/# 1. Store camera credentials (saved to ~/.config/axctl/)
axctl auth add 192.168.1.33
# Username: root
# Password: ****
# 2. Verify connection
axctl devices ping 192.168.1.33
# 3. List AOA scenarios
axctl aoa list 192.168.1.33
# 4. Stream live events
axctl events stream 192.168.1.33-f, --format <fmt> Output format: table (default) | json | jsonl | csv | yaml
-v, --verbose Verbose output
--debug Show raw HTTP requests and responses
--dry-run Preview changes without applying them
Examples:
axctl aoa list 192.168.1.33 --format json
axctl fleet status lab --format csv
axctl devices list --format yamlCredentials are stored locally in ~/.config/axctl/ via the system config store. Passwords are never sent in plaintext — all camera communication uses HTTP Digest Authentication.
axctl auth add <ip> # prompt for username + password
axctl auth add <ip> -u root -p pass # non-interactive
axctl auth list # show all stored credentials
axctl auth remove <ip> # delete credentials for a cameraaxctl devices info <ip> # model, firmware, serial, hardware ID
axctl devices ping <ip> # connectivity check
axctl devices list # list all cameras with stored credentialsExample output (devices info):
┌─────────────┬───────────────────────────────┐
│ field │ value │
├─────────────┼───────────────────────────────┤
│ model │ AXIS Q6358-LE PTZ │
│ firmware │ 12.7.61 │
│ serial │ ACCC8E012345 │
│ hardwareId │ 9F4.1 │
└─────────────┴───────────────────────────────┘
Finds Axis cameras on the local network using mDNS and SSDP.
axctl discover # 5-second scan (default)
axctl discover -t 10000 # 10-second scan
axctl discover --format jsonExample output:
┌──────────────┬───────────────┬──────────┬───────────────┐
│ ip │ model │ firmware │ mac │
├──────────────┼───────────────┼──────────┼───────────────┤
│ 192.168.1.33 │ AXIS Q6358-LE │ 12.7.61 │ AC:CC:8E:xx │
└──────────────┴───────────────┴──────────┴───────────────┘
Manage scenarios on a single camera. Requires AOA to be installed and running (axctl apps start <ip> objectanalytics).
axctl aoa list <ip> # list all scenarios
axctl aoa devices <ip> # list analytics devices (channels)
axctl aoa capabilities <ip> # supported types, max scenarios, object classes# Create a scenario
axctl aoa create <ip> <name> <type>
axctl aoa create 192.168.1.33 "North Entrance" fence
axctl aoa create 192.168.1.33 "Lobby" occupancyInArea --objects human
axctl aoa create 192.168.1.33 "Gate" crosslinecounting --objects human,vehicle --device 1
# Rename or remove
axctl aoa rename 192.168.1.33 2 "Main Gate"
axctl aoa remove 192.168.1.33 2Supported scenario types:
| Type | Trigger | Use case |
|---|---|---|
motion |
includeArea | General motion in a zone |
fence |
fence line | Perimeter crossing |
crosslinecounting |
counting line | Bidirectional people/vehicle counting |
occupancyInArea |
includeArea | Zone occupancy monitoring |
tailgating |
fence line | Closely following persons |
fallDetection |
includeArea | Person fall detection |
Supported object classes: human, vehicle, missing_hardhat
axctl aoa counts 192.168.1.33 2 # accumulated crosslinecounting data
axctl aoa occupancy 192.168.1.33 3 # current occupancy count
axctl aoa reset 192.168.1.33 2 # reset accumulated countsaxctl aoa alarm 192.168.1.33 1 # fire a 3-second test alarm (triggers action rules)Stream AOA detection events over WebSocket. Events fire when objects enter/exit zones, cross lines, or trigger other scenario conditions.
axctl events stream <ip>
axctl events stream <ip> --scenario 1,3 # only scenarios 1 and 3
axctl events stream <ip> --count 10 # stop after 10 events
axctl events stream <ip> --active-only # trigger-start events onlyExample output:
time scenario type objectId class active
09:14:22 1 motion obj-42 human true
09:14:23 1 motion obj-42 human false
09:14:31 3 motion obj-57 vehicle true
Press Ctrl+C to stop streaming.
axctl apps list <ip> # list installed apps + status
axctl apps start <ip> objectanalytics # start AXIS Object Analytics
axctl apps stop <ip> objectanalytics # stop an app
axctl apps start <ip> vmd # start Video Motion DetectionExample output (apps list):
┌──────────────────┬─────────┬─────────┬─────────────────┐
│ name │ package │ status │ version │
├──────────────────┼─────────┼─────────┼─────────────────┤
│ Object Analytics │ objecta │ Running │ 4.5.3-16 │
└──────────────────┴─────────┴─────────┴─────────────────┘
Group cameras into named fleets and run operations across all of them in parallel.
axctl fleet create lab --devices 192.168.1.33,192.168.1.34
axctl fleet create site-a --from-discover # auto-populate from mDNS scan
axctl fleet list
axctl fleet show lab
axctl fleet delete labAll fleet commands use Promise.allSettled — a camera going offline or missing credentials returns an error row without blocking the rest.
axctl fleet ping lab # reachability + RTT per camera
axctl fleet status lab # model/firmware/serial across fleetExample (fleet status):
┌──────────────┬───────────────┬──────────┬──────────────┐
│ ip │ model │ firmware │ serial │
├──────────────┼───────────────┼──────────┼──────────────┤
│ 192.168.1.33 │ AXIS Q6358-LE │ 12.7.61 │ ACCC8E01234 │
│ 192.168.1.34 │ AXIS P3245-V │ 11.8.4 │ ACCC8E05678 │
└──────────────┴───────────────┴──────────┴──────────────┘
axctl fleet aoa list lab # scenarios across all cameras
axctl fleet aoa counts lab 2 # aggregate crossing counts (scenario ID 2)
axctl fleet aoa create lab "Perimeter" fence # push same scenario to all camerasExample (fleet aoa list):
┌──────────────┬────┬──────────────────┬────────┬────────────────┬─────────────┐
│ ip │ id │ name │ type │ objects │ trigger │
├──────────────┼────┼──────────────────┼────────┼────────────────┼─────────────┤
│ 192.168.1.33 │ 1 │ Default Motion │ motion │ vehicle, human │ includeArea │
│ 192.168.1.33 │ 3 │ Entrance │ motion │ human, vehicle │ includeArea │
│ 192.168.1.34 │ 1 │ Perimeter │ fence │ human │ fence │
└──────────────┴────┴──────────────────┴────────┴────────────────┴─────────────┘
src/
├── index.ts # entry point
├── cli/ # Commander.js command groups
│ ├── auth.ts # credential management
│ ├── devices.ts # device info + ping
│ ├── discover.ts # mDNS + SSDP discovery
│ ├── fleet.ts # fleet CRUD + parallel ops
│ ├── analytics.ts # AOA scenario management
│ ├── apps.ts # ACAP app control
│ └── events.ts # WebSocket event streaming
├── lib/ # core logic
│ ├── vapix-client.ts # VAPIX HTTP API wrapper
│ ├── aoa-client.ts # AOA VAPIX API (getConfiguration/setConfiguration)
│ ├── apps-client.ts # ACAP app list/start/stop
│ ├── event-stream.ts # WebSocket event stream + auth
│ ├── fleet-ops.ts # Promise.allSettled parallel execution engine
│ ├── fleet-store.ts # persistent fleet storage (Conf)
│ ├── credential-store.ts# persistent credential storage (Conf)
│ ├── digest-auth.ts # HTTP Digest Authentication implementation
│ └── discovery.ts # mDNS + SSDP camera discovery
├── formatters/
│ └── index.ts # table/json/jsonl/csv/yaml output pipeline
└── tests/
├── formatters.test.ts # 14 tests
├── digest-auth.test.ts # 6 tests
├── aoa-client.test.ts # 17 tests
└── fleet-ops.test.ts # 8 tests
Key design notes:
- No network agent / sidecar — pure CLI, stateless per invocation. Credentials and fleets persist in
~/.config/axctl/via Conf. - AOA write model — Axis Object Analytics uses full-replace configuration (
getConfiguration→ modify →setConfiguration). There are no per-resource PATCH endpoints. - Digest Auth — implemented from scratch in
digest-auth.ts. Supportsqop=authand plain Digest (MD5). The ACAP app control endpoint requires form-encoded requests with a separate digest handshake. - WebSocket events — session token obtained via
GET /axis-cgi/wssession.cgi(15s TTL), used in WebSocket URL. Subscription sent asevents:configureafter connection.
bun install # install dependencies
bun run dev # run from source (no compile step)
bun test # run test suite (45 tests)
bun run typecheck # TypeScript strict check
bun run build # compile native binary → ./axctl
bun run build:all # all 4 platform binaries → dist/- AXIS Q6358-LE (ARTPEC-9, AXIS OS 12.7.61)
- AXIS Object Analytics 4.5.3-16
- Axis camera running AXIS OS 10.x or later
- AXIS Object Analytics installed (for
aoaandeventscommands) - Cameras reachable on the local network (HTTP, port 80)
- Credentials with at least Viewer access (Operator recommended for write operations)
See CONTRIBUTING.md for development setup, code style, and how to submit changes.
Created by Matthew at AlphaVision