feat: Add support for config and initialized conditions (#1894)
These conditions are probably not hugely useful for most card users, but allows the inbuilt automation systems to be used for card internal operations. [skip ci]
This commit is contained in:
1 parent
7fb710d45d
commit
da79664a67
10 files changed
+313
-35
No files matched your search
@@ -184,6 +184,18 @@ export class ConfigManager {
|
||||
await this._api.getDefaultManager().initializeIfNecessary(previousConfig);
|
||||
await this._api.getMediaPlayerManager().initializeIfNecessary(previousConfig);
|
||||
|
||||
// The config is only set in the state if the card is already fully
|
||||
// initialized. If not, the config will be set post initialization in the
|
||||
// InitializationManager.
|
||||
if (
|
||||
this._overriddenConfig &&
|
||||
this._api.getInitializationManager().isInitializedMandatory()
|
||||
) {
|
||||
this._api.getConditionStateManager().setState({
|
||||
config: this._overriddenConfig,
|
||||
});
|
||||
}
|
||||
|
||||
this._api.getCardElementManager().update();
|
||||
}
|
||||
}
|
||||
@@ -128,6 +128,18 @@ export class InitializationManager {
|
||||
}
|
||||
|
||||
this._everInitialized = true;
|
||||
|
||||
// When the card is initialized, both the initialization state (will never
|
||||
// change again), and the config are set in the condition state. The
|
||||
// config is set here, rather than in the ConfigManager, in order to
|
||||
// ensure actions (that trigger on config change) are not run before hass
|
||||
// is available and the card is initialzied (the first config is set in
|
||||
// the card *before* hass is set in the card).
|
||||
this._api.getConditionStateManager().setState({
|
||||
config: config,
|
||||
initialized: this._everInitialized,
|
||||
});
|
||||
|
||||
this._api.getCardElementManager().update();
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +176,7 @@ export interface CardInitializerAPI {
|
||||
getMicrophoneManager(): MicrophoneManager;
|
||||
|
||||
getCardElementManager(): CardElementManager;
|
||||
getConditionStateManager(): ConditionStateManager;
|
||||
getConfigManager(): ConfigManager;
|
||||
getDefaultManager(): DefaultManager;
|
||||
getEntityRegistryManager(): EntityRegistryManager;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { getConfigValue } from '../config/management';
|
||||
import { AdvancedCameraCardCondition } from '../config/types';
|
||||
import { isCompanionApp } from '../utils/companion';
|
||||
import {
|
||||
@@ -146,8 +147,12 @@ export class ConditionsManager implements ConditionsManagerReadonlyInterface {
|
||||
return {
|
||||
result:
|
||||
!!newState?.state &&
|
||||
((!condition.state && !condition.state_not) ||
|
||||
(condition.entity in newState.state &&
|
||||
((!condition.state &&
|
||||
!condition.state_not &&
|
||||
newState.state[condition.entity]?.state !==
|
||||
oldState?.state?.[condition.entity]?.state) ||
|
||||
((!!condition.state || !!condition.state_not) &&
|
||||
condition.entity in newState.state &&
|
||||
(!condition.state ||
|
||||
(Array.isArray(condition.state)
|
||||
? condition.state.includes(newState.state[condition.entity].state)
|
||||
@@ -281,6 +286,29 @@ export class ConditionsManager implements ConditionsManagerReadonlyInterface {
|
||||
(condition.user_agent_re === undefined ||
|
||||
new RegExp(condition.user_agent_re).test(newState.userAgent)),
|
||||
};
|
||||
case 'config': {
|
||||
return {
|
||||
result:
|
||||
!!newState?.config &&
|
||||
newState.config !== oldState?.config &&
|
||||
(!condition.paths?.length ||
|
||||
condition.paths.some(
|
||||
(key) =>
|
||||
getConfigValue(newState.config!, key) !==
|
||||
(oldState?.config ? getConfigValue(oldState?.config, key) : undefined),
|
||||
)),
|
||||
data: {
|
||||
config: {
|
||||
...((oldState?.config || newState?.config) && {
|
||||
...(oldState?.config && { from: oldState?.config }),
|
||||
...(newState?.config && { to: newState?.config }),
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case 'initialized':
|
||||
return { result: !!newState?.initialized };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
import { CurrentUser } from '@dermotduffy/custom-card-helpers';
|
||||
import { HassEntities } from 'home-assistant-js-websocket';
|
||||
import { ViewDisplayMode } from '../config/types';
|
||||
import { AdvancedCameraCardConfig, ViewDisplayMode } from '../config/types';
|
||||
import { MediaLoadedInfo } from '../types';
|
||||
import { KeysState, MicrophoneState } from '../card-controller/types';
|
||||
|
||||
export interface ConditionState {
|
||||
camera?: string;
|
||||
config?: AdvancedCameraCardConfig;
|
||||
displayMode?: ViewDisplayMode;
|
||||
expand?: boolean;
|
||||
fullscreen?: boolean;
|
||||
initialized?: boolean;
|
||||
interaction?: boolean;
|
||||
keys?: KeysState;
|
||||
mediaLoadedInfo?: MediaLoadedInfo | null;
|
||||
@@ -43,10 +45,16 @@ interface ConditionsEvaluationDataState extends ConditionsEvaluationDataFromTo {
|
||||
entity: string;
|
||||
}
|
||||
|
||||
interface ConditionsEvaluationDataConfig {
|
||||
from?: AdvancedCameraCardConfig;
|
||||
to?: AdvancedCameraCardConfig;
|
||||
}
|
||||
|
||||
export interface ConditionsEvaluationData {
|
||||
camera?: ConditionsEvaluationDataFromTo;
|
||||
view?: ConditionsEvaluationDataFromTo;
|
||||
state?: ConditionsEvaluationDataState;
|
||||
config?: ConditionsEvaluationDataConfig;
|
||||
}
|
||||
export interface ConditionsEvaluationResult {
|
||||
result: boolean;
|
||||
|
||||
+39
-30
@@ -683,43 +683,33 @@ export type StatusBarItem = z.infer<typeof statusBarItemSchema>;
|
||||
// Custom Element Configuration: Conditions
|
||||
// *************************************************************************
|
||||
|
||||
const viewConditionSchema = z.object({
|
||||
condition: z.literal('view'),
|
||||
views: z.string().array().optional(),
|
||||
});
|
||||
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().optional(),
|
||||
});
|
||||
const mediaLoadedConditionSchema = z.object({
|
||||
condition: z.literal('media_loaded'),
|
||||
media_loaded: z.boolean(),
|
||||
const configConditionSchema = z.object({
|
||||
condition: z.literal('config'),
|
||||
paths: z.string().array().optional(),
|
||||
});
|
||||
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 expandConditionSchema = z.object({
|
||||
condition: z.literal('expand'),
|
||||
expand: z.boolean(),
|
||||
});
|
||||
const fullscreenConditionSchema = z.object({
|
||||
condition: z.literal('fullscreen'),
|
||||
fullscreen: z.boolean(),
|
||||
});
|
||||
const initializedConditionSchema = z.object({
|
||||
condition: z.literal('initialized'),
|
||||
});
|
||||
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(),
|
||||
});
|
||||
const keyConditionSchema = z.object({
|
||||
condition: z.literal('key'),
|
||||
key: z.string(),
|
||||
@@ -729,6 +719,19 @@ const keyConditionSchema = z.object({
|
||||
alt: z.boolean().optional(),
|
||||
meta: z.boolean().optional(),
|
||||
});
|
||||
const mediaLoadedConditionSchema = z.object({
|
||||
condition: z.literal('media_loaded'),
|
||||
media_loaded: z.boolean(),
|
||||
});
|
||||
const microphoneConditionSchema = z.object({
|
||||
condition: z.literal('microphone'),
|
||||
connected: z.boolean().optional(),
|
||||
muted: z.boolean().optional(),
|
||||
});
|
||||
const triggeredConditionSchema = z.object({
|
||||
condition: z.literal('triggered'),
|
||||
triggered: z.string().array(),
|
||||
});
|
||||
const userAgentConditionSchema = z.object({
|
||||
condition: z.literal('user_agent'),
|
||||
user_agent: z.string().optional(),
|
||||
@@ -748,26 +751,32 @@ const userAgentConditionSchema = z.object({
|
||||
.optional(),
|
||||
companion: z.boolean().optional(),
|
||||
});
|
||||
const viewConditionSchema = z.object({
|
||||
condition: z.literal('view'),
|
||||
views: z.string().array().optional(),
|
||||
});
|
||||
|
||||
export const advancedCameraCardConditionSchema = z.discriminatedUnion('condition', [
|
||||
// Stock conditions:
|
||||
stateConditionSchema,
|
||||
numericStateConditionSchema,
|
||||
screenConditionSchema,
|
||||
stateConditionSchema,
|
||||
usersConditionSchema,
|
||||
|
||||
// Custom conditions:
|
||||
viewConditionSchema,
|
||||
fullscreenConditionSchema,
|
||||
expandConditionSchema,
|
||||
cameraConditionSchema,
|
||||
mediaLoadedConditionSchema,
|
||||
configConditionSchema,
|
||||
displayModeConditionSchema,
|
||||
triggeredConditionSchema,
|
||||
expandConditionSchema,
|
||||
fullscreenConditionSchema,
|
||||
initializedConditionSchema,
|
||||
interactionConditionSchema,
|
||||
microphoneConditionSchema,
|
||||
keyConditionSchema,
|
||||
mediaLoadedConditionSchema,
|
||||
microphoneConditionSchema,
|
||||
triggeredConditionSchema,
|
||||
userAgentConditionSchema,
|
||||
viewConditionSchema,
|
||||
]);
|
||||
export type AdvancedCameraCardCondition = z.infer<
|
||||
typeof advancedCameraCardConditionSchema
|
||||
|
||||
Reference in new issue
Block a user