122 lines
4.2 KiB
TypeScript
122 lines
4.2 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
import { isRecord } from '../../../utils/basic';
|
|
import type { RawAdvancedCameraCardConfig } from '../../types';
|
|
import { performActionActionSchema } from '../actions/stock/perform-action';
|
|
|
|
export const ptzCameraConfigDefaults = {
|
|
r2c_delay_between_calls_seconds: 0.5,
|
|
c2r_delay_between_calls_seconds: 0.2,
|
|
};
|
|
|
|
// Converts WebRTC `data_*` shorthand keys into full `actions_*` perform-action
|
|
// objects (e.g. `data_start_left` → `actions_left_start`, `data_end_left` →
|
|
// `actions_left_stop`, `data_left` → `actions_left`, `data_home` → preset).
|
|
// See: https://github.com/AlexxIT/WebRTC/blob/master/custom_components/webrtc/www/webrtc-camera.js
|
|
const dataPTZFormatToFullFormat =
|
|
(suffix: string) =>
|
|
(data: unknown): unknown => {
|
|
if (!isRecord(data) || !data['service']) {
|
|
return data;
|
|
}
|
|
|
|
const service = data['service'];
|
|
const out: RawAdvancedCameraCardConfig = { ...data };
|
|
|
|
for (const key of Object.keys(data)) {
|
|
const webrtc = key.match(/^data_(start|end)_(.+)$/);
|
|
const name = webrtc
|
|
? `${webrtc[2]}_${webrtc[1] === 'end' ? 'stop' : webrtc[1]}`
|
|
: key.match(/^data_(.+)$/)?.[1];
|
|
|
|
if (!name) {
|
|
continue;
|
|
}
|
|
|
|
// Route `data_home` into a `home` preset listed first so the PTZ
|
|
// home button (which activates the first preset) uses it.
|
|
if (suffix && name === 'home') {
|
|
const presets = isRecord(out['presets']) ? out['presets'] : {};
|
|
if (!('home' in presets)) {
|
|
out['presets'] = {
|
|
home: {
|
|
action: 'perform-action',
|
|
perform_action: service,
|
|
data: out[key],
|
|
},
|
|
...presets,
|
|
};
|
|
}
|
|
} else if (!(`${suffix}${name}` in out)) {
|
|
out[`${suffix}${name}`] = {
|
|
action: 'perform-action',
|
|
perform_action: service,
|
|
data: out[key],
|
|
};
|
|
}
|
|
|
|
delete out[key];
|
|
delete out['service'];
|
|
}
|
|
return out;
|
|
};
|
|
|
|
export const ptzCameraConfigSchema = z.preprocess(
|
|
dataPTZFormatToFullFormat('actions_'),
|
|
z
|
|
.object({
|
|
actions_left: performActionActionSchema.optional(),
|
|
actions_left_start: performActionActionSchema.optional(),
|
|
actions_left_stop: performActionActionSchema.optional(),
|
|
|
|
actions_right: performActionActionSchema.optional(),
|
|
actions_right_start: performActionActionSchema.optional(),
|
|
actions_right_stop: performActionActionSchema.optional(),
|
|
|
|
actions_up: performActionActionSchema.optional(),
|
|
actions_up_start: performActionActionSchema.optional(),
|
|
actions_up_stop: performActionActionSchema.optional(),
|
|
|
|
actions_down: performActionActionSchema.optional(),
|
|
actions_down_start: performActionActionSchema.optional(),
|
|
actions_down_stop: performActionActionSchema.optional(),
|
|
|
|
actions_zoom_in: performActionActionSchema.optional(),
|
|
actions_zoom_in_start: performActionActionSchema.optional(),
|
|
actions_zoom_in_stop: performActionActionSchema.optional(),
|
|
|
|
actions_zoom_out: performActionActionSchema.optional(),
|
|
actions_zoom_out_start: performActionActionSchema.optional(),
|
|
actions_zoom_out_stop: performActionActionSchema.optional(),
|
|
|
|
// The number of seconds between subsequent relative calls when converting a
|
|
// relative request into a continuous request.
|
|
r2c_delay_between_calls_seconds: z
|
|
.number()
|
|
.default(ptzCameraConfigDefaults.r2c_delay_between_calls_seconds),
|
|
|
|
// The number of seconds between the start/stop call when converting a
|
|
// continuous request into a relative request.
|
|
c2r_delay_between_calls_seconds: z
|
|
.number()
|
|
.default(ptzCameraConfigDefaults.c2r_delay_between_calls_seconds),
|
|
|
|
presets: z
|
|
.preprocess(
|
|
dataPTZFormatToFullFormat(''),
|
|
z.union([
|
|
z.record(z.string(), performActionActionSchema),
|
|
|
|
// This is used by the data_ style of action.
|
|
z.object({ service: z.string().optional() }),
|
|
]),
|
|
)
|
|
.optional(),
|
|
|
|
// This is used by the data_ style of action.
|
|
service: z.string().optional(),
|
|
})
|
|
// We allow passthrough as there may be user-configured presets as "actions_<preset>" .
|
|
.passthrough(),
|
|
);
|