onvif-ptz-cli
Version:
Node.js cli tool for controlling PTZ cameras via the ONVIF protocol.
211 lines (202 loc) • 6.08 kB
JavaScript
#! /usr/bin/env node
import _ from "lodash";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { printTable } from "../lib/table.js";
import ptz from "../lib/onvif/ptz.js";
import { createDevice } from "../lib/onvif/device.js";
import fs from "fs";
const argv = yargs(hideBin(process.argv))
.scriptName("")
.usage("Usage: onvif-ptz <command>")
.options({
baseUrl: {
describe: "The baseUrl of the camera.",
demandOption: true,
type: "string",
},
path: {
describe: "The onvif path of the camera.",
type: "string",
default: "/onvif/device_service",
},
username: {
alias: "u",
describe: "The username of the camera.",
demandOption: true,
type: "string",
},
password: {
alias: "p",
describe: "The password of the camera.",
demandOption: true,
type: "string",
},
})
.command("goto-preset", "Move the camera to a preset.", {
preset: {
describe: "The preset which should be loaded.",
demandOption: true,
type: "string",
},
})
.command("goto-home", "Move the camera to the home position.")
.command("move", "Move relatively to the current camera position.", {
x: {
describe:
"Movement on the x-axis. Left: between -1 and 0. Right: between 0 and 1. Optionally add ':NUMBER' for speed (between 0 and 1).",
type: "string",
default: "0",
},
y: {
describe:
"Movement on the y-axis. Down: between -1 and 0. Up: between 0 and 1. Optionally add ':NUMBER' for speed (between 0 and 1).",
type: "string",
default: "0",
},
z: {
describe:
"Movement on the z-axis (zoom). Zoom out: between -1 and 0. Zoom in: between 0 and 1. Optionally add ':NUMBER' for speed (between 0 and 1).",
type: "string",
default: "0",
},
})
.command(
"get-presets",
"List all camera presets. Note: for some cameras the ONVIF presets are separate from the normal presets. If that is the case, you may have to re-create your presets using the set-preset command.",
)
.command("set-preset", "Store the current location/settings in a preset.", {
preset: {
describe: "The token of the preset. Usually just a number.",
demandOption: true,
type: "string",
},
name: {
describe: "The name of the preset.",
type: "string",
},
})
.command("fetch-snapshot", "Fetch a snapshot and safe it to disk.", {
file: {
describe: "The output path for the snapshot jpg file.",
default: "snapshot.jpg",
type: "string",
},
name: {
describe: "The name of the preset.",
type: "string",
},
})
.example(
"onvif-ptz goto-preset --baseUrl=http://192.168.0.123 -u=admin -p=admin --preset=1",
'Load preset "1".',
)
.example(
"onvif-ptz goto-preset --baseUrl=http://192.168.0.123:8080 -u=admin -p=admin --preset=1",
'Load preset "1" on a camera that uses port 8080 as onvif port.',
)
.example(
"onvif-ptz goto-home --baseUrl=http://192.168.0.123 -u=admin -p=admin",
"Move the camera to the home position.",
)
.example(
"onvif-ptz move --baseUrl=http://192.168.0.123:8080 -u=admin -p=admin -x=0.01",
"Move the camera 0.01 to the right.",
)
.example(
"onvif-ptz move --baseUrl=http://192.168.0.123:8080 -u=admin -p=admin -x=0.01:0.5",
"Move the camera 0.01 to the right with half speed.",
)
.example(
"onvif-ptz move --baseUrl=http://192.168.0.123:8080 -u=admin -p=admin -x=0.01 -y=0.02 -z=0.03",
"Move the camera 0.01 to the right, 0.02 to the top and zoom in 0.03.",
)
.example(
"onvif-ptz get-presets --baseUrl=http://192.168.0.123 -u=admin -p=admin",
"List all camera presets.",
)
.example(
"onvif-ptz set-preset --baseUrl=http://192.168.0.123 -u=admin -p=admin --preset=1 --name=Overview",
"Create a preset with preset token 1 and name Overview.",
)
.demandCommand()
.env("ONVIF")
.wrap(null).argv;
(async () => {
try {
const command = argv._[0];
const device = await createDevice({
baseUrl: argv.baseUrl,
path: argv.path,
username: argv.username,
password: argv.password,
});
switch (command) {
case "goto-preset":
await gotoPresetCommand(device, argv);
break;
case "goto-home":
await gotoHomePositionCommand(device);
break;
case "move":
await moveCommand(device, argv);
break;
case "get-presets":
await getPresetsCommand(device);
break;
case "set-preset":
await setPresetCommand(device, argv);
break;
case "fetch-snapshot":
await fetchSnapshotCommand(device, argv);
break;
}
} catch (e) {
console.error(e);
}
})();
async function gotoPresetCommand(device, argv) {
await ptz.gotoPreset(device, {
preset: argv.preset,
});
}
async function gotoHomePositionCommand(device) {
await ptz.gotoHomePosition(device);
}
async function moveCommand(device, argv) {
const x = argv.x.split(":");
const y = argv.y.split(":");
const z = argv.z.split(":");
await ptz.relativeMove(device, {
movement: {
x: parseFloat(x[0]),
y: parseFloat(y[0]),
z: parseFloat(z[0]),
},
speed: {
x: parseFloat(x[1] || 1),
y: parseFloat(y[1] || 1),
z: parseFloat(z[1] || 1),
},
});
}
async function getPresetsCommand(device) {
const presets = (await ptz.getPresets(device)).map((item) => ({
token: _.get(item, "$.token"),
name: item.Name,
x: _.get(item, "PTZPosition.PanTilt.$.x"),
y: _.get(item, "PTZPosition.PanTilt.$.y"),
z: _.get(item, "PTZPosition.Zoom.$.x"),
}));
printTable(presets);
}
async function setPresetCommand(device, argv) {
await ptz.setPreset(device, {
preset: argv.preset,
name: argv.name,
});
}
async function fetchSnapshotCommand(device, argv) {
const result = await device.fetchSnapshot();
fs.writeFileSync(argv.file, result.body, { encoding: "binary" });
}