Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,38 @@ I will be happy to answer any questions and hear your feedback.

---

# Connection

Before you can use most library methods, call `connect()` on your `Onvif` instance. This method performs
the initial handshake with the device and fills internal state so later SOAP requests are authenticated and
routed to the correct service endpoints.

`connect()` runs the following steps in order:

1. **Time synchronization** — `getSystemDateAndTime()` is called first. ONVIF WS-Security authentication
includes a timestamp in the nonce digest, so the client must know the offset between its own clock and
the device clock (`timeShift`). The library tries an unauthenticated request first, as the ONVIF spec allows,
and retries with credentials when the device requires authentication (some Panasonic and Digital Barriers models
behave this way).
2. **Service discovery** — the library tries `device.getServices()`, the modern ONVIF approach introduced
with Profile T. If that fails on older devices, it falls back to `device.getCapabilities()`. Both methods
populate `onvif.uri` with the URLs for media, PTZ, events, replay, and other services that subsequent requests use.
3. **Media configuration** — `media.getProfiles()` and `media.getVideoSources()` run in parallel,
then `getActiveSources()` matches each video source to a suitable media profile. This sets `activeSource`,
`defaultProfile`, and `defaultProfiles`, including encoder settings and PTZ configuration, so you can start
streaming or controlling the camera without extra setup.

On success, `connect()` emits a `connect` event and returns the `Onvif` instance. Pass `autoConnect: true`
in the constructor to run this automatically after instantiation.

```ts
const onvif = new Onvif({ hostname: '192.168.1.13', port: 8000, username: 'admin', password: 'admin' });
await onvif.connect();
console.log(onvif.activeSource);
```

---

# Interfaces

Interfaces are generated according to the latest version of the [ONVIF specification](https://github.com/onvif/specs).
Expand Down
158 changes: 73 additions & 85 deletions src/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
* @see https://www.onvif.org/ver20/analytics/wsdl/analytics.wsdl
*/

import { Onvif } from './onvif';
import { build, linerase, toOnvifXMLSchemaObject } from './utils';
import { Onvif, OnvifServices } from './onvif';
import { toOnvifXMLSchemaObject } from './utils';
import Service from './service';
import { Config, SupportedAnalyticsModules, SupportedRules } from './interfaces/onvif';
import {
Capabilities,
Expand All @@ -27,16 +28,12 @@ import {
ModifyRules,
} from './interfaces/analytics.2';

const ANALYTICS_XMLNS = 'http://www.onvif.org/ver20/analytics/wsdl';

/**
* Analytics service
*/
export class Analytics {
private readonly onvif: Onvif;

constructor(onvif: Onvif) {
this.onvif = onvif;
export class Analytics extends Service {
constructor(onvif: Onvif, service: keyof OnvifServices) {
super(onvif, service);
}

private static configsToBuild(configs?: Config[]) {
Expand All @@ -58,104 +55,98 @@ export class Analytics {
* Returns the capabilities of the analytics service.
*/
async getServiceCapabilities(): Promise<Capabilities> {
const body = build({
GetServiceCapabilities: {
$: { xmlns: ANALYTICS_XMLNS },
},
const response = await this.request({
GetServiceCapabilities: {},
});
const [data] = await this.onvif.request({ service: 'analytics', body });
return linerase(data).getServiceCapabilitiesResponse?.capabilities ?? {};
return response.getServiceCapabilitiesResponse?.capabilities ?? {};
}

/**
* Returns the types of rules supported by the device for a video analytics configuration.
* @param options
*/
async getSupportedRules({ configurationToken }: GetSupportedRules): Promise<SupportedRules> {
const body = build({
GetSupportedRules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
const response = await this.request(
{
GetSupportedRules: {
ConfigurationToken: configurationToken,
},
},
});
const [data] = await this.onvif.request({ service: 'analytics', body });
return linerase(data, { array: ['ruleDescription'] }).getSupportedRulesResponse.supportedRules;
{ array: ['ruleDescription'] },
);
return response.getSupportedRulesResponse.supportedRules;
}

/**
* Creates one or more rules on a video analytics configuration.
* @param options
*/
async createRules({ configurationToken, rule }: CreateRules): Promise<void> {
const body = build({
await this.request({
CreateRules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
Rule: Analytics.configsToBuild(rule),
},
});
await this.onvif.request({ service: 'analytics', body });
}

/**
* Deletes rules from a video analytics configuration.
* @param options
*/
async deleteRules({ configurationToken, ruleName }: DeleteRules): Promise<void> {
const body = build({
await this.request({
DeleteRules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
RuleName: Analytics.namesToBuild(ruleName),
},
});
await this.onvif.request({ service: 'analytics', body });
}

/**
* Modifies rules on a video analytics configuration.
* @param options
*/
async modifyRules({ configurationToken, rule }: ModifyRules): Promise<void> {
const body = build({
await this.request({
ModifyRules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
Rule: Analytics.configsToBuild(rule),
},
});
await this.onvif.request({ service: 'analytics', body });
}

/**
* Returns the rules configured on a video analytics configuration.
* @param options
*/
async getRules({ configurationToken }: GetRules): Promise<Config[]> {
const body = build({
GetRules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
const response = await this.request(
{
GetRules: {
ConfigurationToken: configurationToken,
},
},
});
const [data] = await this.onvif.request({ service: 'analytics', body });
return linerase(data, { array: ['rule'] }).getRulesResponse.rule ?? [];
{ array: ['rule'] },
);
return response.getRulesResponse.rule ?? [];
}

/**
* Returns options for a supported rule type.
* @param options
*/
async getRuleOptions({ configurationToken, ruleType }: GetRuleOptions): Promise<ConfigOptions[]> {
const body = build({
GetRuleOptions: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
...(ruleType !== undefined && { RuleType: ruleType }),
const response = await this.request(
{
GetRuleOptions: {
ConfigurationToken: configurationToken,
...(ruleType !== undefined && { RuleType: ruleType }),
},
},
});
const [data] = await this.onvif.request({ service: 'analytics', body });
return linerase(data, { array: ['ruleOptions'] }).getRuleOptionsResponse.ruleOptions ?? [];
{ array: ['ruleOptions'] },
);
return response.getRuleOptionsResponse.ruleOptions ?? [];
}

/**
Expand All @@ -165,105 +156,102 @@ export class Analytics {
async getSupportedAnalyticsModules({
configurationToken,
}: GetSupportedAnalyticsModules): Promise<SupportedAnalyticsModules> {
const body = build({
GetSupportedAnalyticsModules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
const response = await this.request(
{
GetSupportedAnalyticsModules: {
ConfigurationToken: configurationToken,
},
},
});
const [data] = await this.onvif.request({ service: 'analytics', body });
return linerase(data, { array: ['analyticsModuleDescription'] }).getSupportedAnalyticsModulesResponse
.supportedAnalyticsModules;
{ array: ['analyticsModuleDescription'] },
);
return response.getSupportedAnalyticsModulesResponse.supportedAnalyticsModules;
}

/**
* Creates analytics modules on a video analytics configuration.
* @param options
*/
async createAnalyticsModules({ configurationToken, analyticsModule }: CreateAnalyticsModules): Promise<void> {
const body = build({
await this.request({
CreateAnalyticsModules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
AnalyticsModule: Analytics.configsToBuild(analyticsModule),
},
});
await this.onvif.request({ service: 'analytics', body });
}

/**
* Deletes analytics modules from a video analytics configuration.
* @param options
*/
async deleteAnalyticsModules({ configurationToken, analyticsModuleName }: DeleteAnalyticsModules): Promise<void> {
const body = build({
await this.request({
DeleteAnalyticsModules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
AnalyticsModuleName: Analytics.namesToBuild(analyticsModuleName),
},
});
await this.onvif.request({ service: 'analytics', body });
}

/**
* Returns analytics modules configured on a video analytics configuration.
* @param options
*/
async getAnalyticsModules({ configurationToken }: GetAnalyticsModules): Promise<Config[]> {
const body = build({
GetAnalyticsModules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
const response = await this.request(
{
GetAnalyticsModules: {
ConfigurationToken: configurationToken,
},
},
});
const [data] = await this.onvif.request({ service: 'analytics', body });
return linerase(data, { array: ['analyticsModule'] }).getAnalyticsModulesResponse.analyticsModule ?? [];
{ array: ['analyticsModule'] },
);
return response.getAnalyticsModulesResponse.analyticsModule ?? [];
}

/**
* Modifies analytics modules on a video analytics configuration.
* @param options
*/
async modifyAnalyticsModules({ configurationToken, analyticsModule }: ModifyAnalyticsModules): Promise<void> {
const body = build({
await this.request({
ModifyAnalyticsModules: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
AnalyticsModule: Analytics.configsToBuild(analyticsModule),
},
});
await this.onvif.request({ service: 'analytics', body });
}

/**
* Returns options for a supported analytics module type.
* @param options
*/
async getAnalyticsModuleOptions({ configurationToken, type }: GetAnalyticsModuleOptions): Promise<ConfigOptions[]> {
const body = build({
GetAnalyticsModuleOptions: {
$: { xmlns: ANALYTICS_XMLNS },
ConfigurationToken: configurationToken,
...(type !== undefined && { Type: type }),
const response = await this.request(
{
GetAnalyticsModuleOptions: {
ConfigurationToken: configurationToken,
...(type !== undefined && { Type: type }),
},
},
});
const [data] = await this.onvif.request({ service: 'analytics', body });
return linerase(data, { array: ['options'] }).getAnalyticsModuleOptionsResponse.options ?? [];
{ array: ['options'] },
);
return response.getAnalyticsModuleOptionsResponse.options ?? [];
}

/**
* Returns sample metadata produced by analytics modules.
* @param options
*/
async getSupportedMetadata(options: GetSupportedMetadata = {}): Promise<MetadataInfo[]> {
const body = build({
GetSupportedMetadata: {
$: { xmlns: ANALYTICS_XMLNS },
...(options.type !== undefined && { Type: options.type }),
const response = await this.request(
{
GetSupportedMetadata: {
...(options.type !== undefined && { Type: options.type }),
},
},
});
const [data] = await this.onvif.request({ service: 'analytics', body });
return linerase(data, { array: ['analyticsModule'] }).getSupportedMetadataResponse?.analyticsModule ?? [];
{ array: ['analyticsModule'] },
);
return response.getSupportedMetadataResponse?.analyticsModule ?? [];
}
}
Loading
Loading