refactor: Migrate config schema from Zod v3 to Zod v4 (#2357)
This commit is contained in:
@@ -9,7 +9,7 @@ export const internalCallbackActionConfigSchema =
|
||||
advanced_camera_card_action: z.literal(INTERNAL_CALLBACK_ACTION),
|
||||
|
||||
// The callback is expected to be called with a CardController API object.
|
||||
callback: z.function().args(z.any()).returns(z.promise(z.void())),
|
||||
callback: z.function().input([z.any()]).output(z.promise(z.void())),
|
||||
});
|
||||
export type InternalCallbackActionConfig = z.infer<
|
||||
typeof internalCallbackActionConfigSchema
|
||||
|
||||
@@ -9,7 +9,7 @@ import { targetSchema } from './target';
|
||||
export const callServiceActionSchema = actionBaseSchema.extend({
|
||||
action: z.literal('call-service'),
|
||||
service: z.string(),
|
||||
data: z.object({}).passthrough().optional(),
|
||||
data: z.looseObject({}).optional(),
|
||||
target: targetSchema.optional(),
|
||||
});
|
||||
export type CallServiceActionConfig = z.infer<typeof callServiceActionSchema>;
|
||||
|
||||
@@ -5,5 +5,5 @@ export const customActionSchema = actionBaseSchema
|
||||
.extend({
|
||||
action: z.literal('fire-dom-event'),
|
||||
})
|
||||
.passthrough();
|
||||
.loose();
|
||||
export type CustomActionConfig = z.infer<typeof customActionSchema>;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { targetSchema } from './target';
|
||||
export const performActionActionSchema = actionBaseSchema.extend({
|
||||
action: z.literal('perform-action'),
|
||||
perform_action: z.string(),
|
||||
data: z.object({}).passthrough().optional(),
|
||||
data: z.looseObject({}).optional(),
|
||||
target: targetSchema.optional(),
|
||||
});
|
||||
export type PerformActionActionConfig = z.infer<typeof performActionActionSchema>;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { HassServiceTarget } from 'home-assistant-js-websocket';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const targetSchema: z.ZodSchema<HassServiceTarget, z.ZodTypeDef> = z.object({
|
||||
entity_id: z.string().or(z.string().array()).optional(),
|
||||
device_id: z.string().or(z.string().array()).optional(),
|
||||
area_id: z.string().or(z.string().array()).optional(),
|
||||
floor_id: z.string().or(z.string().array()).optional(),
|
||||
label_id: z.string().or(z.string().array()).optional(),
|
||||
export const targetSchema: z.ZodSchema<HassServiceTarget> = z.object({
|
||||
entity_id: z.union([z.string(), z.string().array()]).optional(),
|
||||
device_id: z.union([z.string(), z.string().array()]).optional(),
|
||||
area_id: z.union([z.string(), z.string().array()]).optional(),
|
||||
label_id: z.union([z.string(), z.string().array()]).optional(),
|
||||
floor_id: z.union([z.string(), z.string().array()]).optional(),
|
||||
});
|
||||
|
||||
@@ -29,18 +29,15 @@ export type StatusBarActionConfig = z.infer<
|
||||
status_bar_action: 'add' | 'remove' | 'reset';
|
||||
items?: StatusBarItem[];
|
||||
};
|
||||
export const statusBarActionConfigSchema: z.ZodSchema<
|
||||
StatusBarActionConfig,
|
||||
z.ZodTypeDef,
|
||||
unknown
|
||||
> = advancedCameraCardCustomActionsBaseSchema.extend({
|
||||
advanced_camera_card_action: z.literal('status_bar'),
|
||||
status_bar_action: z.enum(['add', 'remove', 'reset']),
|
||||
items: z
|
||||
.lazy(() => statusBarItemSchema)
|
||||
.array()
|
||||
.optional(),
|
||||
});
|
||||
export const statusBarActionConfigSchema: z.ZodSchema<StatusBarActionConfig> =
|
||||
advancedCameraCardCustomActionsBaseSchema.extend({
|
||||
advanced_camera_card_action: z.literal('status_bar'),
|
||||
status_bar_action: z.enum(['add', 'remove', 'reset']),
|
||||
items: z
|
||||
.lazy(() => statusBarItemSchema)
|
||||
.array()
|
||||
.optional(),
|
||||
});
|
||||
|
||||
const advancedCameraCardCustomActionSchema = z.union([
|
||||
cameraSelectActionConfigSchema,
|
||||
@@ -69,7 +66,6 @@ export const actionConfigSchema = z.union([
|
||||
advancedCameraCardCustomActionSchema,
|
||||
]);
|
||||
export type ActionConfig = z.infer<typeof actionConfigSchema>;
|
||||
|
||||
export const actionsBaseSchema = z
|
||||
.object({
|
||||
tap_action: actionConfigSchema.or(actionConfigSchema.array()).optional(),
|
||||
@@ -81,7 +77,7 @@ export const actionsBaseSchema = z
|
||||
// Passthrough to allow (at least) entity/camera_image to go through. This
|
||||
// card doesn't need these attributes, but handleAction() in
|
||||
// custom_card_helpers may depending on how the action is configured.
|
||||
.passthrough();
|
||||
.loose();
|
||||
export type Actions = z.infer<typeof actionsBaseSchema>;
|
||||
|
||||
export interface AuxillaryActionConfig {
|
||||
|
||||
@@ -77,7 +77,7 @@ export const ptzCameraConfigSchema = z.preprocess(
|
||||
.preprocess(
|
||||
dataPTZFormatToFullFormat(''),
|
||||
z.union([
|
||||
z.record(performActionActionSchema),
|
||||
z.record(z.string(), performActionActionSchema),
|
||||
|
||||
// This is used by the data_ style of action.
|
||||
z.object({ service: z.string().optional() }),
|
||||
|
||||
@@ -4,8 +4,8 @@ import { mediaLayoutConfigSchema } from './camera/media-layout';
|
||||
import { ptzCameraConfigDefaults, ptzCameraConfigSchema } from './camera/ptz';
|
||||
import { aspectRatioSchema } from './common/aspect-ratio';
|
||||
import { eventsMediaTypeSchema } from './common/events-media';
|
||||
import { imageBaseConfigDefault, imageBaseConfigSchema } from './common/image';
|
||||
import { severitySchema } from './common/severity';
|
||||
import { imageBaseConfigSchema, imageConfigDefault } from './common/image';
|
||||
|
||||
const CAMERA_TRIGGER_EVENT_TYPES = [
|
||||
// An event whether or not it has any media yet associated with it.
|
||||
@@ -56,7 +56,7 @@ const webrtcCardConfigSchema = z
|
||||
entity: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
.loose();
|
||||
|
||||
const jsmpegConfigSchema = z.object({
|
||||
options: z
|
||||
@@ -150,6 +150,8 @@ export const cameraConfigDefault = {
|
||||
ssl_ciphers: 'auto' as const,
|
||||
ssl_verification: 'auto' as const,
|
||||
},
|
||||
go2rtc: go2rtcConfigDefault,
|
||||
image: imageBaseConfigDefault,
|
||||
always_error_if_entity_unavailable: false,
|
||||
};
|
||||
|
||||
@@ -217,7 +219,7 @@ const cameraMediaConfigSchema = z.object({
|
||||
});
|
||||
|
||||
export const cameraConfigSchema = z
|
||||
.object({
|
||||
.looseObject({
|
||||
camera_entity: z.string().optional(),
|
||||
|
||||
// Used for presentation in the UI (autodetected from the entity if
|
||||
@@ -318,7 +320,7 @@ export const cameraConfigSchema = z
|
||||
// Live provider options.
|
||||
live_provider: z.enum(LIVE_PROVIDERS).default(cameraConfigDefault.live_provider),
|
||||
go2rtc: go2rtcConfigSchema.optional().default(go2rtcConfigDefault),
|
||||
image: imageBaseConfigSchema.optional().default(imageConfigDefault),
|
||||
image: imageBaseConfigSchema.optional().default(imageBaseConfigDefault),
|
||||
jsmpeg: jsmpegConfigSchema.optional(),
|
||||
webrtc_card: webrtcCardConfigSchema.optional(),
|
||||
|
||||
|
||||
@@ -22,6 +22,6 @@ export const ptzControlsConfigSchema = z.object({
|
||||
hide_zoom: z.boolean().default(ptzControlsDefaults.hide_zoom),
|
||||
hide_home: z.boolean().default(ptzControlsDefaults.hide_home),
|
||||
|
||||
style: z.object({}).passthrough().optional(),
|
||||
style: z.looseObject({}).optional(),
|
||||
});
|
||||
export type PTZControlsConfig = z.infer<typeof ptzControlsConfigSchema>;
|
||||
|
||||
@@ -5,7 +5,7 @@ export const THUMBNAIL_WIDTH_MIN = 75;
|
||||
export const THUMBNAIL_WIDTH_DEFAULT = 100;
|
||||
export const THUMBNAIL_WIDTH_MAX = 300;
|
||||
|
||||
const thumbnailControlsBaseDefaults = {
|
||||
export const thumbnailControlsBaseDefaults = {
|
||||
size: THUMBNAIL_WIDTH_DEFAULT,
|
||||
show_details: true,
|
||||
show_favorite_control: true,
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const imageConfigDefault = {
|
||||
export const imageBaseConfigDefault = {
|
||||
mode: 'auto' as const,
|
||||
refresh_seconds: 1,
|
||||
};
|
||||
|
||||
export const imageConfigDefault = {
|
||||
...imageBaseConfigDefault,
|
||||
zoomable: true,
|
||||
};
|
||||
|
||||
@@ -10,11 +14,13 @@ const IMAGE_MODES = ['auto', 'camera', 'entity', 'screensaver', 'url'] as const;
|
||||
export type ImageMode = (typeof IMAGE_MODES)[number];
|
||||
|
||||
export const imageBaseConfigSchema = z.object({
|
||||
mode: z.enum(IMAGE_MODES).default(imageConfigDefault.mode),
|
||||
mode: z.enum(IMAGE_MODES).default(imageBaseConfigDefault.mode),
|
||||
|
||||
refresh_seconds: z.number().min(0).default(imageConfigDefault.refresh_seconds),
|
||||
refresh_seconds: z.number().min(0).default(imageBaseConfigDefault.refresh_seconds),
|
||||
|
||||
url: z.string().optional(),
|
||||
entity: z.string().optional(),
|
||||
entity_parameters: z.string().optional(),
|
||||
});
|
||||
|
||||
export type ImageBaseConfig = z.infer<typeof imageBaseConfigSchema>;
|
||||
|
||||
@@ -10,4 +10,3 @@ export const stockConditionSchema = z.discriminatedUnion('condition', [
|
||||
screenConditionSchema,
|
||||
usersConditionSchema,
|
||||
]);
|
||||
export type StockCondition = z.infer<typeof stockConditionSchema>;
|
||||
|
||||
@@ -2,6 +2,8 @@ import { z } from 'zod';
|
||||
import { actionsBaseSchema } from '../actions/types';
|
||||
|
||||
export const elementsBaseSchema = actionsBaseSchema.extend({
|
||||
style: z.record(z.string().nullable().or(z.undefined()).or(z.number())).optional(),
|
||||
style: z
|
||||
.record(z.string(), z.string().nullable().or(z.undefined()).or(z.number()))
|
||||
.optional(),
|
||||
title: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
@@ -11,5 +11,6 @@ export const menuBaseSchema = z.object({
|
||||
.optional(),
|
||||
alignment: z.enum(['matching', 'opposing']).default('matching').optional(),
|
||||
icon: z.string().optional(),
|
||||
state_color: z.boolean().default(true).optional(),
|
||||
permanent: z.boolean().default(false).optional(),
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
||||
import { iconSchema } from '../../stock/icon';
|
||||
import { menuBaseSchema } from './base';
|
||||
|
||||
export const menuIconSchema = menuBaseSchema.merge(iconSchema).extend({
|
||||
export const menuIconSchema = menuBaseSchema.extend(iconSchema.shape).extend({
|
||||
type: z.literal('custom:advanced-camera-card-menu-icon'),
|
||||
});
|
||||
export type MenuIcon = z.infer<typeof menuIconSchema>;
|
||||
|
||||
@@ -3,9 +3,9 @@ import { stateIconSchema } from '../../stock/state-icon';
|
||||
import { menuBaseSchema } from './base';
|
||||
|
||||
export const menuStateIconSchema = menuBaseSchema
|
||||
.merge(stateIconSchema)
|
||||
.extend(stateIconSchema.shape)
|
||||
.extend({
|
||||
type: z.literal('custom:advanced-camera-card-menu-state-icon'),
|
||||
})
|
||||
.merge(menuBaseSchema);
|
||||
.extend(menuBaseSchema.shape);
|
||||
export type MenuStateIcon = z.infer<typeof menuStateIconSchema>;
|
||||
|
||||
@@ -3,8 +3,10 @@ import { stateIconSchema } from '../../stock/state-icon';
|
||||
import { menuBaseSchema } from './base';
|
||||
import { menuSubmenuItemSchema } from './submenu';
|
||||
|
||||
export const menuSubmenuSelectSchema = menuBaseSchema.merge(stateIconSchema).extend({
|
||||
type: z.literal('custom:advanced-camera-card-menu-submenu-select'),
|
||||
options: z.record(menuSubmenuItemSchema.deepPartial()).optional(),
|
||||
});
|
||||
export const menuSubmenuSelectSchema = menuBaseSchema
|
||||
.extend(stateIconSchema.shape)
|
||||
.extend({
|
||||
type: z.literal('custom:advanced-camera-card-menu-submenu-select'),
|
||||
options: z.record(z.string(), menuSubmenuItemSchema.partial()).optional(),
|
||||
});
|
||||
export type MenuSubmenuSelect = z.infer<typeof menuSubmenuSelectSchema>;
|
||||
|
||||
@@ -13,7 +13,7 @@ export const menuSubmenuItemSchema = elementsBaseSchema.extend({
|
||||
});
|
||||
export type MenuSubmenuItem = z.infer<typeof menuSubmenuItemSchema>;
|
||||
|
||||
export const menuSubmenuSchema = menuBaseSchema.merge(iconSchema).extend({
|
||||
export const menuSubmenuSchema = menuBaseSchema.extend(iconSchema.shape).extend({
|
||||
type: z.literal('custom:advanced-camera-card-menu-submenu'),
|
||||
items: menuSubmenuItemSchema.array(),
|
||||
});
|
||||
|
||||
@@ -7,11 +7,11 @@ export const customSchema = z
|
||||
type: z.string().superRefine((val, ctx) => {
|
||||
if (!val.match(/^custom:(?!advanced-camera-card).+/)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
code: 'custom',
|
||||
message: 'advanced-camera-card custom elements must match specific schemas',
|
||||
fatal: true,
|
||||
});
|
||||
}
|
||||
}),
|
||||
})
|
||||
.passthrough();
|
||||
.loose();
|
||||
|
||||
@@ -8,8 +8,8 @@ export const imageSchema = elementsBaseSchema.extend({
|
||||
image: z.string().optional(),
|
||||
camera_image: z.string().optional(),
|
||||
camera_view: z.string().optional(),
|
||||
state_image: z.object({}).passthrough().optional(),
|
||||
state_image: z.looseObject({}).optional(),
|
||||
filter: z.string().optional(),
|
||||
state_filter: z.object({}).passthrough().optional(),
|
||||
state_filter: z.looseObject({}).optional(),
|
||||
aspect_ratio: z.string().optional(),
|
||||
});
|
||||
|
||||
@@ -8,5 +8,5 @@ export const serviceCallButtonSchema = elementsBaseSchema.extend({
|
||||
// Title is required for service button.
|
||||
title: z.string(),
|
||||
service: z.string(),
|
||||
service_data: z.object({}).passthrough().optional(),
|
||||
service_data: z.looseObject({}).optional(),
|
||||
});
|
||||
|
||||
@@ -4,11 +4,8 @@ import {
|
||||
statusBarImageItemSchema,
|
||||
statusBarStringItemSchema,
|
||||
} from '../actions/types';
|
||||
import { StockCondition, stockConditionSchema } from '../conditions/stock/types';
|
||||
import {
|
||||
AdvancedCameraCardCondition,
|
||||
advancedCameraCardConditionSchema,
|
||||
} from '../conditions/types';
|
||||
import { stockConditionSchema } from '../conditions/stock/types';
|
||||
import { advancedCameraCardConditionSchema } from '../conditions/types';
|
||||
import { menuIconSchema } from './custom/menu/icon';
|
||||
import { menuStateIconSchema } from './custom/menu/state-icon';
|
||||
import { menuSubmenuSchema } from './custom/menu/submenu';
|
||||
@@ -25,36 +22,30 @@ import { stateLabelSchema } from './stock/state-label';
|
||||
// include other elements. Putting these elements elsewhere would cause
|
||||
// typescript circular dependency errors as the types need to be both included
|
||||
// in the master pictureElementSchema, but also refer to it internally.
|
||||
//
|
||||
// Provide a manual type definition to avoid the `any` that would be created by
|
||||
// the lazy() evaluation below.
|
||||
// See: https://zod.dev/?id=recursive-types
|
||||
|
||||
// https://www.home-assistant.io/lovelace/picture-elements/#image-element
|
||||
type Conditional = {
|
||||
type: 'conditional';
|
||||
conditions: StockCondition[];
|
||||
elements?: PictureElements;
|
||||
};
|
||||
export const conditionalSchema: z.ZodSchema<Conditional, z.ZodTypeDef> = z.object({
|
||||
export const conditionalSchema = z.object({
|
||||
type: z.literal('conditional'),
|
||||
conditions: stockConditionSchema.array(),
|
||||
elements: z.lazy(() => pictureElementsSchema),
|
||||
|
||||
get elements() {
|
||||
// Recursive schema.
|
||||
return pictureElementsSchema;
|
||||
},
|
||||
});
|
||||
|
||||
export type AdvancedCameraCardConditional = {
|
||||
type: 'custom:advanced-camera-card-conditional';
|
||||
conditions: AdvancedCameraCardCondition[];
|
||||
elements?: PictureElements;
|
||||
};
|
||||
const advancedCameraCardConditionalSchema: z.ZodSchema<
|
||||
AdvancedCameraCardConditional,
|
||||
z.ZodTypeDef
|
||||
> = z.object({
|
||||
const advancedCameraCardConditionalSchema = z.object({
|
||||
type: z.literal('custom:advanced-camera-card-conditional'),
|
||||
conditions: advancedCameraCardConditionSchema.array(),
|
||||
elements: z.lazy(() => pictureElementsSchema),
|
||||
|
||||
get elements() {
|
||||
// Recursive schema.
|
||||
return pictureElementsSchema;
|
||||
},
|
||||
});
|
||||
export type AdvancedCameraCardConditional = z.infer<
|
||||
typeof advancedCameraCardConditionalSchema
|
||||
>;
|
||||
|
||||
// Cannot use discriminatedUnion since customSchema uses a superRefine, which
|
||||
// causes false rejections.
|
||||
@@ -77,5 +68,5 @@ const pictureElementSchema = z.union([
|
||||
statusBarStringItemSchema,
|
||||
]);
|
||||
|
||||
export const pictureElementsSchema = pictureElementSchema.array().optional();
|
||||
export const pictureElementsSchema = pictureElementSchema.array().optional().default([]);
|
||||
export type PictureElements = z.infer<typeof pictureElementsSchema>;
|
||||
|
||||
@@ -60,14 +60,15 @@ const titleMatcherSchema = z.object({
|
||||
});
|
||||
export type TitleMatcher = z.infer<typeof titleMatcherSchema>;
|
||||
|
||||
type OrMatcher = {
|
||||
type: 'or';
|
||||
matchers: Matcher[];
|
||||
};
|
||||
const orMatcherSchema: z.ZodSchema<OrMatcher, z.ZodTypeDef> = z.object({
|
||||
const orMatcherSchema = z.object({
|
||||
type: z.literal('or'),
|
||||
matchers: z.array(z.lazy(() => matcherSchema)),
|
||||
|
||||
get matchers() {
|
||||
// Recursive schema.
|
||||
return z.array(matcherSchema);
|
||||
},
|
||||
});
|
||||
|
||||
export const matcherSchema = z.union([
|
||||
dateMatcherSchema,
|
||||
orMatcherSchema,
|
||||
|
||||
@@ -6,7 +6,7 @@ export const imageConfigSchema = imageBaseConfigSchema
|
||||
.extend({
|
||||
zoomable: z.boolean().default(imageConfigDefault.zoomable),
|
||||
})
|
||||
.merge(actionsSchema)
|
||||
.extend(actionsSchema.shape)
|
||||
.default(imageConfigDefault);
|
||||
|
||||
export type ImageViewConfig = z.infer<typeof imageConfigSchema>;
|
||||
|
||||
@@ -52,7 +52,6 @@ export const liveConfigDefault = {
|
||||
zoomable: true,
|
||||
transition_effect: 'slide' as const,
|
||||
show_image_during_load: true,
|
||||
mode: 'single' as const,
|
||||
controls: {
|
||||
builtin: true,
|
||||
next_previous: {
|
||||
@@ -126,6 +125,6 @@ export const liveConfigSchema = z
|
||||
),
|
||||
zoomable: z.boolean().default(liveConfigDefault.zoomable),
|
||||
})
|
||||
.merge(actionsSchema)
|
||||
.extend(actionsSchema.shape)
|
||||
.default(liveConfigDefault);
|
||||
export type LiveConfig = z.infer<typeof liveConfigSchema>;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
import { actionsSchema } from './actions/types';
|
||||
import {
|
||||
thumbnailControlsDefaults,
|
||||
thumbnailControlsBaseDefaults,
|
||||
thumbnailsControlBaseSchema,
|
||||
} from './common/controls/thumbnails';
|
||||
|
||||
const mediaGalleryThumbnailControlsDefaults = {
|
||||
...thumbnailControlsDefaults,
|
||||
...thumbnailControlsBaseDefaults,
|
||||
show_details: false,
|
||||
};
|
||||
|
||||
@@ -43,6 +43,6 @@ export const mediaGalleryConfigSchema = z
|
||||
})
|
||||
.default(mediaGalleryConfigDefault.controls),
|
||||
})
|
||||
.merge(actionsSchema)
|
||||
.extend(actionsSchema.shape)
|
||||
.default(mediaGalleryConfigDefault);
|
||||
export type MediaGalleryConfig = z.infer<typeof mediaGalleryConfigSchema>;
|
||||
|
||||
+36
-27
@@ -13,13 +13,20 @@ const MENU_STYLES = [
|
||||
const MENU_POSITIONS = ['left', 'right', 'top', 'bottom'] as const;
|
||||
const MENU_ALIGNMENTS = MENU_POSITIONS;
|
||||
|
||||
const visibleButtonDefault = {
|
||||
const baseButtonDefault = {
|
||||
alignment: 'matching' as const,
|
||||
state_color: true,
|
||||
permanent: false,
|
||||
priority: MENU_PRIORITY_DEFAULT,
|
||||
};
|
||||
|
||||
const visibleButtonDefault = {
|
||||
...baseButtonDefault,
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
const hiddenButtonDefault = {
|
||||
priority: MENU_PRIORITY_DEFAULT,
|
||||
...baseButtonDefault,
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
@@ -27,35 +34,37 @@ export const menuConfigDefault = {
|
||||
alignment: 'left' as const,
|
||||
button_size: 40,
|
||||
buttons: {
|
||||
camera_ui: visibleButtonDefault,
|
||||
cameras: visibleButtonDefault,
|
||||
clips: hiddenButtonDefault,
|
||||
ptz_home: hiddenButtonDefault,
|
||||
display_mode: visibleButtonDefault,
|
||||
download: visibleButtonDefault,
|
||||
expand: hiddenButtonDefault,
|
||||
folders: visibleButtonDefault,
|
||||
iris: visibleButtonDefault,
|
||||
fullscreen: visibleButtonDefault,
|
||||
image: hiddenButtonDefault,
|
||||
info: visibleButtonDefault,
|
||||
gallery: visibleButtonDefault,
|
||||
live: visibleButtonDefault,
|
||||
media_player: visibleButtonDefault,
|
||||
// Clone per key so each button has its own default object. This avoids
|
||||
// shared nested default references between keys.
|
||||
camera_ui: { ...visibleButtonDefault },
|
||||
cameras: { ...visibleButtonDefault },
|
||||
clips: { ...hiddenButtonDefault },
|
||||
ptz_home: { ...hiddenButtonDefault },
|
||||
display_mode: { ...visibleButtonDefault },
|
||||
download: { ...visibleButtonDefault },
|
||||
expand: { ...hiddenButtonDefault },
|
||||
folders: { ...visibleButtonDefault },
|
||||
iris: { ...visibleButtonDefault },
|
||||
fullscreen: { ...visibleButtonDefault },
|
||||
image: { ...hiddenButtonDefault },
|
||||
info: { ...visibleButtonDefault },
|
||||
gallery: { ...visibleButtonDefault },
|
||||
live: { ...visibleButtonDefault },
|
||||
media_player: { ...visibleButtonDefault },
|
||||
microphone: {
|
||||
...hiddenButtonDefault,
|
||||
type: 'momentary' as const,
|
||||
},
|
||||
mute: hiddenButtonDefault,
|
||||
play: hiddenButtonDefault,
|
||||
ptz_controls: hiddenButtonDefault,
|
||||
recordings: hiddenButtonDefault,
|
||||
reviews: hiddenButtonDefault,
|
||||
set_review: visibleButtonDefault,
|
||||
screenshot: hiddenButtonDefault,
|
||||
snapshots: hiddenButtonDefault,
|
||||
substreams: visibleButtonDefault,
|
||||
timeline: visibleButtonDefault,
|
||||
mute: { ...hiddenButtonDefault },
|
||||
play: { ...hiddenButtonDefault },
|
||||
ptz_controls: { ...hiddenButtonDefault },
|
||||
recordings: { ...hiddenButtonDefault },
|
||||
reviews: { ...hiddenButtonDefault },
|
||||
set_review: { ...visibleButtonDefault },
|
||||
screenshot: { ...hiddenButtonDefault },
|
||||
snapshots: { ...hiddenButtonDefault },
|
||||
substreams: { ...visibleButtonDefault },
|
||||
timeline: { ...visibleButtonDefault },
|
||||
},
|
||||
position: 'top' as const,
|
||||
style: 'hidden' as const,
|
||||
|
||||
@@ -3,8 +3,8 @@ import { advancedCameraCardConditionSchema } from './conditions/types';
|
||||
|
||||
const overrideSchema = z.object({
|
||||
conditions: advancedCameraCardConditionSchema.array(),
|
||||
merge: z.object({}).passthrough().optional(),
|
||||
set: z.object({}).passthrough().optional(),
|
||||
merge: z.looseObject({}).optional(),
|
||||
set: z.looseObject({}).optional(),
|
||||
delete: z.string().array().optional(),
|
||||
});
|
||||
export type Override = z.infer<typeof overrideSchema>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { deepRemoveDefaults } from '../../utils/zod';
|
||||
import { deepRemoveDefaults } from '../../utils/zod/deep-remove-defaults';
|
||||
import { automationsSchema } from './automations';
|
||||
import { cameraConfigDefault, cameraConfigSchema, camerasConfigSchema } from './cameras';
|
||||
import { cardIDRegex } from './common/const';
|
||||
|
||||
@@ -45,6 +45,8 @@ export type PTZKeyboardShortcutName =
|
||||
| 'ptz_zoom_in'
|
||||
| 'ptz_zoom_out';
|
||||
|
||||
const interactionModeDefault = 'inactive' as const;
|
||||
|
||||
export const viewConfigDefault = {
|
||||
default: 'auto' as const,
|
||||
camera_select: 'current' as const,
|
||||
@@ -53,7 +55,7 @@ export const viewConfigDefault = {
|
||||
every_seconds: 0,
|
||||
after_interaction: false,
|
||||
entities: [],
|
||||
interaction_mode: 'inactive' as const,
|
||||
interaction_mode: interactionModeDefault,
|
||||
},
|
||||
default_cycle_camera: false,
|
||||
dim: false,
|
||||
@@ -64,6 +66,7 @@ export const viewConfigDefault = {
|
||||
show_trigger_status: false,
|
||||
filter_selected_camera: true,
|
||||
actions: {
|
||||
interaction_mode: interactionModeDefault,
|
||||
trigger: 'update' as const,
|
||||
untrigger: 'none' as const,
|
||||
},
|
||||
@@ -73,7 +76,9 @@ export const viewConfigDefault = {
|
||||
keyboard_shortcuts: keyboardShortcutsDefault,
|
||||
};
|
||||
|
||||
const interactionModeSchema = z.enum(['all', 'inactive', 'active']).default('inactive');
|
||||
const interactionModeSchema = z
|
||||
.enum(['all', 'inactive', 'active'])
|
||||
.default(interactionModeDefault);
|
||||
export type InteractionMode = z.infer<typeof interactionModeSchema>;
|
||||
|
||||
export const triggersSchema = z.object({
|
||||
@@ -108,7 +113,7 @@ export type ThemeName = z.infer<typeof themeName>;
|
||||
|
||||
const themeConfigSchema = z.object({
|
||||
themes: themeName.array().default(viewConfigDefault.theme.themes),
|
||||
overrides: z.record(z.string()).optional(),
|
||||
overrides: z.record(z.string(), z.string()).optional(),
|
||||
});
|
||||
export type ThemeConfig = z.infer<typeof themeConfigSchema>;
|
||||
|
||||
@@ -128,9 +133,7 @@ export const viewConfigSchema = z
|
||||
.default(viewConfigDefault.default_reset.after_interaction),
|
||||
every_seconds: z.number().default(viewConfigDefault.default_reset.every_seconds),
|
||||
entities: z.string().array().default(viewConfigDefault.default_reset.entities),
|
||||
interaction_mode: interactionModeSchema.default(
|
||||
viewConfigDefault.default_reset.interaction_mode,
|
||||
),
|
||||
interaction_mode: interactionModeSchema,
|
||||
})
|
||||
.default(viewConfigDefault.default_reset),
|
||||
|
||||
@@ -144,5 +147,5 @@ export const viewConfigSchema = z
|
||||
viewConfigDefault.keyboard_shortcuts,
|
||||
),
|
||||
})
|
||||
.merge(actionsSchema)
|
||||
.extend(actionsSchema.shape)
|
||||
.default(viewConfigDefault);
|
||||
|
||||
@@ -27,7 +27,6 @@ export const viewerConfigDefault = {
|
||||
zoomable: true,
|
||||
transition_effect: 'slide' as const,
|
||||
snapshot_click_plays_clip: true,
|
||||
display_mode: 'single' as const,
|
||||
controls: {
|
||||
builtin: true,
|
||||
next_previous: {
|
||||
@@ -106,6 +105,6 @@ export const viewerConfigSchema = z
|
||||
})
|
||||
.default(viewerConfigDefault.controls),
|
||||
})
|
||||
.merge(actionsSchema)
|
||||
.extend(actionsSchema.shape)
|
||||
.default(viewerConfigDefault);
|
||||
export type ViewerConfig = z.infer<typeof viewerConfigSchema>;
|
||||
|
||||
Reference in New Issue
Block a user