Convert conditions to HA style.
This commit is contained in:
@@ -38,7 +38,7 @@ export class AutomationsManager {
|
||||
for (const automation of this._automations ?? []) {
|
||||
const shouldExecute = this._api
|
||||
.getConditionsManager()
|
||||
.evaluateCondition(automation.conditions);
|
||||
.evaluateConditions(automation.conditions);
|
||||
const actions = shouldExecute ? automation.actions : automation.actions_not;
|
||||
const priorEvaluation = this._priorEvaluations.get(automation);
|
||||
this._priorEvaluations.set(automation, shouldExecute);
|
||||
|
||||
@@ -5,13 +5,17 @@ import { copyConfig } from '../config-mgmt';
|
||||
import {
|
||||
FrigateCardCondition,
|
||||
frigateConditionalSchema,
|
||||
MicrophoneConditionState,
|
||||
OverrideConfigurationKey,
|
||||
RawFrigateCardConfig,
|
||||
ViewDisplayMode,
|
||||
} from '../config/types';
|
||||
import { CardConditionAPI } from './types';
|
||||
|
||||
interface MicrophoneConditionState {
|
||||
connected?: boolean;
|
||||
muted?: boolean;
|
||||
}
|
||||
|
||||
interface ConditionState {
|
||||
view?: string;
|
||||
fullscreen?: boolean;
|
||||
@@ -26,13 +30,13 @@ interface ConditionState {
|
||||
user?: CurrentUser;
|
||||
}
|
||||
|
||||
export class ConditionEvaluateRequestEvent extends Event {
|
||||
public condition: FrigateCardCondition;
|
||||
export class ConditionsEvaluateRequestEvent extends Event {
|
||||
public conditions: FrigateCardCondition[];
|
||||
public evaluation?: boolean;
|
||||
|
||||
constructor(condition: FrigateCardCondition, eventInitDict?: EventInit) {
|
||||
super('frigate-card:condition:evaluate', eventInitDict);
|
||||
this.condition = condition;
|
||||
constructor(conditions: FrigateCardCondition[], eventInitDict?: EventInit) {
|
||||
super('frigate-card:conditions:evaluate', eventInitDict);
|
||||
this.conditions = conditions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,13 +46,13 @@ export class ConditionEvaluateRequestEvent extends Event {
|
||||
*/
|
||||
export function evaluateConditionViaEvent(
|
||||
element: HTMLElement,
|
||||
condition?: FrigateCardCondition,
|
||||
conditions?: FrigateCardCondition[],
|
||||
): boolean {
|
||||
if (!condition) {
|
||||
if (!conditions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const evaluateEvent = new ConditionEvaluateRequestEvent(condition, {
|
||||
const evaluateEvent = new ConditionsEvaluateRequestEvent(conditions, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
});
|
||||
@@ -70,7 +74,7 @@ export function evaluateConditionViaEvent(
|
||||
}
|
||||
|
||||
type RawOverrides = {
|
||||
conditions: FrigateCardCondition;
|
||||
conditions: FrigateCardCondition[];
|
||||
overrides: RawFrigateCardConfig;
|
||||
}[];
|
||||
|
||||
@@ -84,7 +88,7 @@ export function getOverriddenConfig(
|
||||
let overridden = false;
|
||||
if (configOverrides) {
|
||||
for (const override of configOverrides) {
|
||||
if (manager.evaluateCondition(override.conditions, stateOverrides)) {
|
||||
if (manager.evaluateConditions(override.conditions, stateOverrides)) {
|
||||
merge(output, override.overrides);
|
||||
overridden = true;
|
||||
}
|
||||
@@ -156,7 +160,7 @@ export class ConditionsManager {
|
||||
const getAllConditions = (): FrigateCardCondition[] => {
|
||||
const config = this._api.getConfigManager().getConfig();
|
||||
const conditions: FrigateCardCondition[] = [];
|
||||
config?.overrides?.forEach((override) => conditions.push(override.conditions));
|
||||
config?.overrides?.forEach((override) => conditions.push(...override.conditions));
|
||||
|
||||
// Element conditions can be arbitrarily nested underneath conditionals and
|
||||
// custom elements that this card may not known. Here we recursively parse
|
||||
@@ -164,7 +168,7 @@ export class ConditionsManager {
|
||||
const getElementsConditions = (data: unknown): void => {
|
||||
const parseResult = frigateConditionalSchema.safeParse(data);
|
||||
if (parseResult.success) {
|
||||
conditions.push(parseResult.data.conditions);
|
||||
conditions.push(...parseResult.data.conditions);
|
||||
parseResult.data.elements?.forEach(getElementsConditions);
|
||||
} else if (data && typeof data === 'object') {
|
||||
Object.keys(data).forEach((key) => getElementsConditions(data[key]));
|
||||
@@ -175,15 +179,12 @@ export class ConditionsManager {
|
||||
};
|
||||
|
||||
const conditions = getAllConditions();
|
||||
this._hasHAStateConditions = conditions.some(
|
||||
(condition) =>
|
||||
!!condition.state?.length ||
|
||||
!!condition.numeric_state?.length ||
|
||||
!!condition.users?.length,
|
||||
this._hasHAStateConditions = conditions.some((conditionObj) =>
|
||||
['state', 'numeric_state', 'user'].includes(conditionObj.condition),
|
||||
);
|
||||
conditions.forEach((condition) => {
|
||||
if (condition.media_query) {
|
||||
const mql = window.matchMedia(condition.media_query);
|
||||
conditions.forEach((conditionObj) => {
|
||||
if (conditionObj.condition === 'screen') {
|
||||
const mql = window.matchMedia(conditionObj.media_query);
|
||||
mql.addEventListener('change', this._mediaQueryTrigger);
|
||||
this._mediaQueries.push(mql);
|
||||
}
|
||||
@@ -210,8 +211,17 @@ export class ConditionsManager {
|
||||
return this._epoch;
|
||||
}
|
||||
|
||||
public evaluateCondition(
|
||||
condition: Readonly<FrigateCardCondition>,
|
||||
public evaluateConditions(
|
||||
conditions: Readonly<FrigateCardCondition>[],
|
||||
stateOverrides?: Partial<ConditionState>,
|
||||
): boolean {
|
||||
return conditions.every((conditionObj) =>
|
||||
this._evaluateCondition(conditionObj, stateOverrides),
|
||||
);
|
||||
}
|
||||
|
||||
protected _evaluateCondition(
|
||||
conditionObj: Readonly<FrigateCardCondition>,
|
||||
stateOverrides?: Partial<ConditionState>,
|
||||
): boolean {
|
||||
const state = {
|
||||
@@ -219,79 +229,71 @@ export class ConditionsManager {
|
||||
...stateOverrides,
|
||||
};
|
||||
|
||||
let result = true;
|
||||
if (condition.view?.length) {
|
||||
result &&= !!state?.view && condition.view.includes(state.view);
|
||||
}
|
||||
if (condition.fullscreen !== undefined) {
|
||||
result &&=
|
||||
state.fullscreen !== undefined && condition.fullscreen === state.fullscreen;
|
||||
}
|
||||
if (condition.expand !== undefined) {
|
||||
result &&= state.expand !== undefined && condition.expand === state.expand;
|
||||
}
|
||||
if (condition.camera?.length) {
|
||||
result &&= !!state.camera && condition.camera.includes(state.camera);
|
||||
}
|
||||
if (condition.state?.length) {
|
||||
for (const stateTest of condition.state) {
|
||||
result &&=
|
||||
switch (conditionObj.condition) {
|
||||
case 'view':
|
||||
return !!state?.view && conditionObj.views.includes(state.view);
|
||||
case 'fullscreen':
|
||||
return (
|
||||
state.fullscreen !== undefined && conditionObj.fullscreen === state.fullscreen
|
||||
);
|
||||
case 'expand':
|
||||
return state.expand !== undefined && conditionObj.expand === state.expand;
|
||||
case 'camera':
|
||||
return !!state.camera && conditionObj.cameras.includes(state.camera);
|
||||
case 'state':
|
||||
return (
|
||||
!!state.state &&
|
||||
((!stateTest.state && !stateTest.state_not) ||
|
||||
(stateTest.entity in state.state &&
|
||||
(!stateTest.state ||
|
||||
(Array.isArray(stateTest.state)
|
||||
? stateTest.state.includes(state.state[stateTest.entity].state)
|
||||
: stateTest.state === state.state[stateTest.entity].state)) &&
|
||||
(!stateTest.state_not ||
|
||||
(Array.isArray(stateTest.state_not)
|
||||
? !stateTest.state_not.includes(state.state[stateTest.entity].state)
|
||||
: stateTest.state_not !== state.state[stateTest.entity].state))));
|
||||
}
|
||||
}
|
||||
if (condition.numeric_state?.length) {
|
||||
for (const stateTest of condition.numeric_state) {
|
||||
result &&=
|
||||
((!conditionObj.state && !conditionObj.state_not) ||
|
||||
(conditionObj.entity in state.state &&
|
||||
(!conditionObj.state ||
|
||||
(Array.isArray(conditionObj.state)
|
||||
? conditionObj.state.includes(state.state[conditionObj.entity].state)
|
||||
: conditionObj.state === state.state[conditionObj.entity].state)) &&
|
||||
(!conditionObj.state_not ||
|
||||
(Array.isArray(conditionObj.state_not)
|
||||
? !conditionObj.state_not.includes(
|
||||
state.state[conditionObj.entity].state,
|
||||
)
|
||||
: conditionObj.state_not !== state.state[conditionObj.entity].state))))
|
||||
);
|
||||
case 'numeric_state':
|
||||
return (
|
||||
!!state.state &&
|
||||
stateTest.entity in state.state &&
|
||||
state.state[stateTest.entity].state !== undefined &&
|
||||
(stateTest.above === undefined ||
|
||||
Number(state.state[stateTest.entity].state) > stateTest.above) &&
|
||||
(stateTest.below === undefined ||
|
||||
Number(state.state[stateTest.entity].state) < stateTest.below);
|
||||
}
|
||||
conditionObj.entity in state.state &&
|
||||
state.state[conditionObj.entity].state !== undefined &&
|
||||
(conditionObj.above === undefined ||
|
||||
Number(state.state[conditionObj.entity].state) > conditionObj.above) &&
|
||||
(conditionObj.below === undefined ||
|
||||
Number(state.state[conditionObj.entity].state) < conditionObj.below)
|
||||
);
|
||||
case 'user':
|
||||
return !!state.user && conditionObj.users.includes(state.user.id);
|
||||
case 'media_loaded':
|
||||
return (
|
||||
state.media_loaded !== undefined &&
|
||||
conditionObj.media_loaded === state.media_loaded
|
||||
);
|
||||
case 'screen':
|
||||
return window.matchMedia(conditionObj.media_query).matches;
|
||||
case 'display_mode':
|
||||
return !!state.displayMode && conditionObj.display_mode === state.displayMode;
|
||||
case 'triggered':
|
||||
return conditionObj.triggered.some((triggeredCameraID) =>
|
||||
state.triggered?.has(triggeredCameraID),
|
||||
);
|
||||
case 'interaction':
|
||||
return (
|
||||
state.interaction !== undefined &&
|
||||
conditionObj.interaction === state.interaction
|
||||
);
|
||||
case 'microphone':
|
||||
return (
|
||||
(conditionObj.connected === undefined ||
|
||||
state.microphone?.connected === conditionObj.connected) &&
|
||||
(conditionObj.muted === undefined ||
|
||||
state.microphone?.muted === conditionObj.muted)
|
||||
);
|
||||
}
|
||||
if (condition.users?.length) {
|
||||
result &&= !!state.user && condition.users.includes(state.user.id);
|
||||
}
|
||||
if (condition.media_loaded !== undefined) {
|
||||
result &&=
|
||||
state.media_loaded !== undefined &&
|
||||
condition.media_loaded === state.media_loaded;
|
||||
}
|
||||
if (condition.media_query) {
|
||||
result &&= window.matchMedia(condition.media_query).matches;
|
||||
}
|
||||
if (condition.display_mode) {
|
||||
result &&= !!state.displayMode && condition.display_mode === state.displayMode;
|
||||
}
|
||||
if (condition.triggered?.length) {
|
||||
result &&= condition.triggered.some((triggeredCameraID) =>
|
||||
state.triggered?.has(triggeredCameraID),
|
||||
);
|
||||
}
|
||||
if (condition.interaction !== undefined) {
|
||||
result &&=
|
||||
state.interaction !== undefined && condition.interaction === state.interaction;
|
||||
}
|
||||
if (condition.microphone) {
|
||||
result &&=
|
||||
(condition.microphone.connected === undefined ||
|
||||
state.microphone?.connected === condition.microphone.connected) &&
|
||||
(condition.microphone.muted === undefined ||
|
||||
state.microphone?.muted === condition.microphone.muted);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected _createEpoch(): ConditionsManagerEpoch {
|
||||
|
||||
+3
-3
@@ -8,7 +8,7 @@ import { ViewContext } from 'view';
|
||||
import 'web-dialog';
|
||||
import pkg from '../package.json';
|
||||
import { actionHandler } from './action-handler-directive.js';
|
||||
import { ConditionEvaluateRequestEvent } from './card-controller/conditions-manager.js';
|
||||
import { ConditionsEvaluateRequestEvent } from './card-controller/conditions-manager.js';
|
||||
import { CardController } from './card-controller/controller';
|
||||
import { MenuButtonController } from './components-lib/menu-button-controller';
|
||||
import './components/elements.js';
|
||||
@@ -341,10 +341,10 @@ class FrigateCard extends LitElement {
|
||||
this._menuButtonController.removeDynamicMenuButton(ev.detail);
|
||||
this.requestUpdate();
|
||||
}}
|
||||
@frigate-card:condition:evaluate=${(ev: ConditionEvaluateRequestEvent) => {
|
||||
@frigate-card:conditions:evaluate=${(ev: ConditionsEvaluateRequestEvent) => {
|
||||
ev.evaluation = this._controller
|
||||
.getConditionsManager()
|
||||
?.evaluateCondition(ev.condition);
|
||||
?.evaluateConditions(ev.conditions);
|
||||
}}
|
||||
>
|
||||
</frigate-card-elements>`
|
||||
|
||||
+110
-1
@@ -3,8 +3,13 @@ import get from 'lodash-es/get';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import set from 'lodash-es/set';
|
||||
import unset from 'lodash-es/unset';
|
||||
import { RawFrigateCardConfig, RawFrigateCardConfigArray } from './config/types';
|
||||
import {
|
||||
FrigateCardCondition,
|
||||
RawFrigateCardConfig,
|
||||
RawFrigateCardConfigArray,
|
||||
} from './config/types';
|
||||
import {
|
||||
CONF_AUTOMATIONS,
|
||||
CONF_CAMERAS,
|
||||
CONF_CAMERAS_GLOBAL_DIMENSIONS_LAYOUT,
|
||||
CONF_CAMERAS_GLOBAL_IMAGE,
|
||||
@@ -342,6 +347,99 @@ const conditionMediaLoadedTransform = (data: unknown): boolean => {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform a single object with multiple conditions to multiple objects with
|
||||
* single conditions
|
||||
* @param data Input data.
|
||||
* @returns `true` if the configuration was modified.
|
||||
*/
|
||||
const conditionToConditionsTransform = (data: unknown): boolean => {
|
||||
if (
|
||||
typeof data !== 'object' ||
|
||||
!data ||
|
||||
typeof data['conditions'] !== 'object' ||
|
||||
!data['conditions']
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const oldConditions = data['conditions'];
|
||||
const newConditions: FrigateCardCondition[] = [];
|
||||
|
||||
if (oldConditions['view'] !== undefined) {
|
||||
newConditions.push({
|
||||
condition: 'view' as const,
|
||||
views: oldConditions['view'],
|
||||
});
|
||||
}
|
||||
if (oldConditions['fullscreen'] !== undefined) {
|
||||
newConditions.push({
|
||||
condition: 'fullscreen' as const,
|
||||
fullscreen: oldConditions['fullscreen'],
|
||||
});
|
||||
}
|
||||
if (oldConditions['expand'] !== undefined) {
|
||||
newConditions.push({
|
||||
condition: 'expand' as const,
|
||||
expand: oldConditions['expand'],
|
||||
});
|
||||
}
|
||||
if (oldConditions['camera'] !== undefined) {
|
||||
newConditions.push({
|
||||
condition: 'camera' as const,
|
||||
cameras: oldConditions['camera'],
|
||||
});
|
||||
}
|
||||
if (oldConditions['media_loaded'] !== undefined) {
|
||||
newConditions.push({
|
||||
condition: 'media_loaded' as const,
|
||||
media_loaded: oldConditions['media_loaded'],
|
||||
});
|
||||
}
|
||||
if (oldConditions['state'] !== undefined && Array.isArray(oldConditions['state'])) {
|
||||
for (const stateCondition of oldConditions['state']) {
|
||||
if (
|
||||
typeof stateCondition === 'object' &&
|
||||
stateCondition &&
|
||||
(stateCondition['state'] !== undefined ||
|
||||
stateCondition['state_not'] !== undefined ||
|
||||
stateCondition['entity'] !== undefined)
|
||||
) {
|
||||
newConditions.push({
|
||||
condition: 'state' as const,
|
||||
...(stateCondition['state'] && {
|
||||
state: stateCondition['state'],
|
||||
}),
|
||||
...(stateCondition['state_not'] && {
|
||||
state_not: stateCondition['state_not'],
|
||||
}),
|
||||
...(stateCondition['entity'] && {
|
||||
entity: stateCondition['entity'],
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldConditions['media_query'] !== undefined) {
|
||||
newConditions.push({
|
||||
condition: 'screen' as const,
|
||||
media_query: oldConditions['media_query'],
|
||||
});
|
||||
}
|
||||
|
||||
// These conditions did not exist prior to v6.0.0 and so are not converted:
|
||||
// - display_mode
|
||||
// - triggered
|
||||
// - interaction
|
||||
// - microphone
|
||||
|
||||
if (newConditions.length) {
|
||||
data['conditions'] = newConditions;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform service_data -> data
|
||||
* See: https://github.com/dermotduffy/frigate-hass-card/issues/1103
|
||||
@@ -569,4 +667,15 @@ const UPGRADES = [
|
||||
upgradeMoveToWithOverrides('live.layout', CONF_CAMERAS_GLOBAL_DIMENSIONS_LAYOUT),
|
||||
deleteWithOverrides('media_viewer.layout'),
|
||||
deleteWithOverrides('image.layout'),
|
||||
upgradeArrayOfObjects(CONF_OVERRIDES, conditionToConditionsTransform),
|
||||
(data: unknown): boolean => {
|
||||
return upgradeObjectRecursively(conditionToConditionsTransform)(
|
||||
typeof data === 'object' && data ? data[CONF_ELEMENTS] : {},
|
||||
);
|
||||
},
|
||||
(data: unknown): boolean => {
|
||||
return upgradeObjectRecursively(conditionToConditionsTransform)(
|
||||
typeof data === 'object' && data ? data[CONF_AUTOMATIONS] : {},
|
||||
);
|
||||
},
|
||||
];
|
||||
|
||||
+66
-33
@@ -401,7 +401,7 @@ const imageSchema = elementsBaseSchema.extend({
|
||||
});
|
||||
|
||||
// https://www.home-assistant.io/dashboards/conditional/#state
|
||||
const stateCondition = z.object({
|
||||
const stateConditionSchema = z.object({
|
||||
condition: z.literal('state'),
|
||||
entity: z.string(),
|
||||
state: z.string().or(z.string().array()).optional(),
|
||||
@@ -409,38 +409,36 @@ const stateCondition = z.object({
|
||||
});
|
||||
|
||||
// https://www.home-assistant.io/dashboards/conditional/#numeric-state
|
||||
const numericCondition = z.object({
|
||||
const numericStateConditionSchema = z.object({
|
||||
condition: z.literal('numeric_state'),
|
||||
entity: z.string(),
|
||||
above: z.number().optional(),
|
||||
below: z.number().optional(),
|
||||
});
|
||||
|
||||
const mediaQuerySchema = z.string();
|
||||
|
||||
// https://www.home-assistant.io/dashboards/conditional/#screen
|
||||
const screenCondition = z.object({
|
||||
const screenConditionSchema = z.object({
|
||||
condition: z.literal('screen'),
|
||||
media_query: mediaQuerySchema,
|
||||
media_query: z.string(),
|
||||
});
|
||||
|
||||
// https://www.home-assistant.io/dashboards/conditional/#user
|
||||
const usersCondition = z.object({
|
||||
const usersConditionSchema = z.object({
|
||||
condition: z.literal('user'),
|
||||
users: z.string().array().min(1),
|
||||
});
|
||||
|
||||
const stockCondition = z.union([
|
||||
stateCondition,
|
||||
numericCondition,
|
||||
screenCondition,
|
||||
usersCondition,
|
||||
const stockConditionSchema = z.discriminatedUnion('condition', [
|
||||
stateConditionSchema,
|
||||
numericStateConditionSchema,
|
||||
screenConditionSchema,
|
||||
usersConditionSchema,
|
||||
]);
|
||||
|
||||
// https://www.home-assistant.io/lovelace/picture-elements/#image-element
|
||||
export const conditionalSchema = z.object({
|
||||
type: z.literal('conditional'),
|
||||
conditions: stockCondition.array(),
|
||||
conditions: stockConditionSchema.array(),
|
||||
elements: z.lazy(() => pictureElementsSchema),
|
||||
});
|
||||
|
||||
@@ -516,32 +514,67 @@ export type MenuItem = MenuIcon | MenuStateIcon | MenuSubmenu | MenuSubmenuSelec
|
||||
// Custom Element Configuration: Conditions
|
||||
// *************************************************************************
|
||||
|
||||
const viewConditionSchema = z.object({
|
||||
condition: z.literal('view'),
|
||||
views: z.string().array(),
|
||||
});
|
||||
const fullscreenConditionSchema = z.object({
|
||||
condition: z.literal('fullscreen'),
|
||||
fullscreen: z.boolean(),
|
||||
});
|
||||
const expandConditionSchema = z.object({
|
||||
condition: z.literal('expand'),
|
||||
expand: z.boolean(),
|
||||
});
|
||||
const cameraConditionSchema = z.object({
|
||||
condition: z.literal('camera'),
|
||||
cameras: z.string().array(),
|
||||
});
|
||||
const mediaLoadedConditionSchema = z.object({
|
||||
condition: z.literal('media_loaded'),
|
||||
media_loaded: z.boolean(),
|
||||
});
|
||||
const displayModeConditionSchema = z.object({
|
||||
condition: z.literal('display_mode'),
|
||||
display_mode: viewDisplayModeSchema,
|
||||
});
|
||||
const triggeredConditionSchema = z.object({
|
||||
condition: z.literal('triggered'),
|
||||
triggered: z.string().array(),
|
||||
});
|
||||
const interactionConditionSchema = z.object({
|
||||
condition: z.literal('interaction'),
|
||||
interaction: z.boolean(),
|
||||
});
|
||||
const microphoneConditionSchema = z.object({
|
||||
condition: z.literal('microphone'),
|
||||
connected: z.boolean().optional(),
|
||||
muted: z.boolean().optional(),
|
||||
});
|
||||
export type MicrophoneConditionState = z.infer<typeof microphoneConditionSchema>;
|
||||
|
||||
export const frigateCardConditionSchema = z.object({
|
||||
view: z.string().array().optional(),
|
||||
fullscreen: z.boolean().optional(),
|
||||
expand: z.boolean().optional(),
|
||||
camera: z.string().array().optional(),
|
||||
media_loaded: z.boolean().optional(),
|
||||
state: stateCondition.array().optional(),
|
||||
numeric_state: numericCondition.array().optional(),
|
||||
media_query: mediaQuerySchema.optional(),
|
||||
display_mode: viewDisplayModeSchema.optional(),
|
||||
triggered: z.string().array().optional(),
|
||||
interaction: z.boolean().optional(),
|
||||
microphone: microphoneConditionSchema.optional(),
|
||||
users: z.string().array().optional()
|
||||
});
|
||||
export const frigateCardConditionSchema = z.discriminatedUnion('condition', [
|
||||
// Stock conditions:
|
||||
stateConditionSchema,
|
||||
numericStateConditionSchema,
|
||||
screenConditionSchema,
|
||||
usersConditionSchema,
|
||||
|
||||
// Custom conditions:
|
||||
viewConditionSchema,
|
||||
fullscreenConditionSchema,
|
||||
expandConditionSchema,
|
||||
cameraConditionSchema,
|
||||
mediaLoadedConditionSchema,
|
||||
displayModeConditionSchema,
|
||||
triggeredConditionSchema,
|
||||
interactionConditionSchema,
|
||||
microphoneConditionSchema,
|
||||
]);
|
||||
export type FrigateCardCondition = z.infer<typeof frigateCardConditionSchema>;
|
||||
|
||||
export const frigateConditionalSchema = z.object({
|
||||
type: z.literal('custom:frigate-card-conditional'),
|
||||
conditions: frigateCardConditionSchema,
|
||||
conditions: frigateCardConditionSchema.array(),
|
||||
elements: z.lazy(() => pictureElementsSchema),
|
||||
});
|
||||
export type FrigateConditional = z.infer<typeof frigateConditionalSchema>;
|
||||
@@ -1034,7 +1067,7 @@ export type LiveConfig = z.infer<typeof liveConfigSchema>;
|
||||
|
||||
const liveOverridesSchema = z
|
||||
.object({
|
||||
conditions: frigateCardConditionSchema,
|
||||
conditions: frigateCardConditionSchema.array(),
|
||||
overrides: liveOverridableConfigSchema,
|
||||
})
|
||||
.array()
|
||||
@@ -1551,7 +1584,7 @@ export type OverrideConfigurationKey = keyof z.infer<typeof overrideConfiguratio
|
||||
|
||||
const overridesSchema = z
|
||||
.object({
|
||||
conditions: frigateCardConditionSchema,
|
||||
conditions: frigateCardConditionSchema.array(),
|
||||
overrides: overrideConfigurationSchema,
|
||||
})
|
||||
.array()
|
||||
@@ -1565,7 +1598,7 @@ const automationActionSchema = actionSchema.array();
|
||||
export type AutomationActions = z.infer<typeof automationActionSchema>;
|
||||
|
||||
const automationSchema = z.object({
|
||||
conditions: frigateCardConditionSchema,
|
||||
conditions: frigateCardConditionSchema.array(),
|
||||
actions: automationActionSchema.optional(),
|
||||
actions_not: automationActionSchema.optional(),
|
||||
});
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export const REPO_URL = 'https://github.com/dermotduffy/frigate-hass-card' as const;
|
||||
export const TROUBLESHOOTING_URL = `${REPO_URL}#troubleshooting` as const;
|
||||
|
||||
export const CONF_AUTOMATIONS = 'automations' as const;
|
||||
|
||||
export const CONF_CAMERAS = 'cameras' as const;
|
||||
export const CONF_CAMERAS_ARRAY_CAMERA_ENTITY =
|
||||
`${CONF_CAMERAS}.#.camera_entity` as const;
|
||||
|
||||
Reference in New Issue
Block a user