Add support for more advanced forms of overriding

This commit is contained in:
Dermot Duffy
2024-04-13 18:23:11 -07:00
parent 0eca5d780c
commit 323925101d
21 changed files with 855 additions and 285 deletions
+60 -31
View File
@@ -1,14 +1,21 @@
import { CurrentUser } from '@dermotduffy/custom-card-helpers';
import { HassEntities } from 'home-assistant-js-websocket';
import merge from 'lodash-es/merge';
import { copyConfig } from '../config/management';
import { ZodSchema } from 'zod';
import {
copyConfig,
deleteConfigValue,
getConfigValue,
setConfigValue,
} from '../config/management';
import {
FrigateCardCondition,
frigateConditionalSchema,
OverrideConfigurationKey,
RawFrigateCardConfig,
ViewDisplayMode,
frigateConditionalSchema,
Overrides,
} from '../config/types';
import { desparsifyArrays } from '../utils/basic';
import { CardConditionAPI } from './types';
interface MicrophoneConditionState {
@@ -73,44 +80,66 @@ export function evaluateConditionViaEvent(
return evaluateEvent.evaluation ?? false;
}
type RawOverrides = {
conditions: FrigateCardCondition[];
overrides: RawFrigateCardConfig;
}[];
export function getOverriddenConfig(
manager: Readonly<ConditionsManager>,
config: Readonly<RawFrigateCardConfig>,
configOverrides?: Readonly<RawOverrides>,
stateOverrides?: Partial<ConditionState>,
options?: {
configOverrides?: Readonly<Overrides>;
stateOverrides?: Partial<ConditionState>;
schema?: ZodSchema;
logOnParseError?: boolean;
},
): RawFrigateCardConfig {
const output = copyConfig(config);
let output = copyConfig(config);
let overridden = false;
if (configOverrides) {
for (const override of configOverrides) {
if (manager.evaluateConditions(override.conditions, stateOverrides)) {
merge(output, override.overrides);
if (options?.configOverrides) {
for (const override of options.configOverrides) {
if (manager.evaluateConditions(override.conditions, options?.stateOverrides)) {
override.delete?.forEach((deletionKey) => {
deleteConfigValue(output, deletionKey);
});
Object.keys(override.set ?? {}).forEach((setKey) => {
setConfigValue(output, setKey, override.set?.[setKey]);
});
Object.keys(override.merge ?? {}).forEach((mergeKey) => {
setConfigValue(
output,
mergeKey,
merge({}, getConfigValue(output, mergeKey), override.merge?.[mergeKey]),
);
});
overridden = true;
}
}
}
// Attempt to return the same configuration object if it has not been
// overridden (to reduce re-renders for a configuration that has not changed).
return overridden ? output : config;
}
export function getOverridesByKey(
key: OverrideConfigurationKey,
overrides?: Readonly<RawOverrides>,
): RawOverrides {
return (
overrides
?.filter((o) => key in o.overrides)
.map((o) => ({
conditions: o.conditions,
overrides: o.overrides[key] as RawFrigateCardConfig,
})) ?? []
);
if (!overridden) {
// Attempt to return the same configuration object if it has not been
// overridden (to reduce re-renders for a configuration that has not changed).
return config;
}
if (options?.configOverrides?.some((override) => override.delete?.length)) {
// If anything was deleted during this override, empty undefined slots may
// be left in arrays where values were unset. Desparsify them.
output = desparsifyArrays(output);
}
if (options?.schema) {
const parseResult = options.schema.safeParse(output);
if (options.logOnParseError && !parseResult.success) {
console.warn(
`Cannot parse overridden configuration`,
output,
parseResult.error.message,
);
}
return parseResult.success ? parseResult.data : config;
}
return output;
}
// A tiny wrapper interface to allow the same manager to be passed around
+6 -6
View File
@@ -4,7 +4,7 @@ import {
CardWideConfig,
FrigateCardConfig,
frigateCardConfigSchema,
RawFrigateCardConfig
RawFrigateCardConfig,
} from '../config/types';
import { localize } from '../localize/localize';
import { setProfiles } from '../config/profiles';
@@ -104,11 +104,11 @@ export class ConfigManager {
if (!this._config) {
return;
}
const overriddenConfig = getOverriddenConfig(
conditionsManager,
this._config,
this._config.overrides,
) as FrigateCardConfig;
const overriddenConfig = getOverriddenConfig(conditionsManager, this._config, {
configOverrides: this._config.overrides,
schema: frigateCardConfigSchema,
logOnParseError: !!this.getCardWideConfig()?.debug?.logging,
}) as FrigateCardConfig;
// Save on Lit re-rendering costs by only updating the configuration if it
// actually changes.