@@ -1,10 +1,12 @@
|
||||
import { ActionContext } from 'action';
|
||||
import { z } from 'zod';
|
||||
import { ConditionsEvaluationData } from '../../conditions/types.js';
|
||||
import { Actions, ActionsConfig, ActionType } from '../../config/types.js';
|
||||
import { getActionConfigGivenAction } from '../../utils/action.js';
|
||||
import { ActionSet } from './actions/set.js';
|
||||
import { TemplateRenderer } from '../templates/index.js';
|
||||
import { CardActionsManagerAPI } from '../types.js';
|
||||
import { ActionSet } from './actions/set.js';
|
||||
import { ActionExecutionRequest, AuxillaryActionConfig } from './types.js';
|
||||
import { ActionContext } from 'action';
|
||||
|
||||
const INTERACTIONS = ['tap', 'double_tap', 'hold', 'start_tap', 'end_tap'] as const;
|
||||
export type InteractionName = (typeof INTERACTIONS)[number];
|
||||
@@ -22,9 +24,11 @@ export class ActionsManager {
|
||||
protected _api: CardActionsManagerAPI;
|
||||
protected _actionsInFlight: ActionSet[] = [];
|
||||
protected _actionContext: ActionContext = {};
|
||||
protected _templateRenderer: TemplateRenderer | null;
|
||||
|
||||
constructor(api: CardActionsManagerAPI) {
|
||||
constructor(api: CardActionsManagerAPI, templateRenderer?: TemplateRenderer) {
|
||||
this._api = api;
|
||||
this._templateRenderer = templateRenderer ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +76,7 @@ export class ActionsManager {
|
||||
// actions).
|
||||
actionConfig
|
||||
) {
|
||||
this.executeActions(actionConfig, config);
|
||||
this.executeActions(actionConfig, { config });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -97,7 +101,9 @@ export class ActionsManager {
|
||||
public handleActionExecutionRequestEvent = async (
|
||||
ev: CustomEvent<ActionExecutionRequest>,
|
||||
): Promise<void> => {
|
||||
await this.executeActions(ev.detail.action, ev.detail.config);
|
||||
await this.executeActions(ev.detail.action, {
|
||||
config: ev.detail.config,
|
||||
});
|
||||
};
|
||||
|
||||
public uninitialize(): void {
|
||||
@@ -107,10 +113,22 @@ export class ActionsManager {
|
||||
|
||||
public async executeActions(
|
||||
action: ActionType | ActionType[],
|
||||
config?: AuxillaryActionConfig,
|
||||
options?: {
|
||||
config?: AuxillaryActionConfig;
|
||||
triggerData?: ConditionsEvaluationData;
|
||||
},
|
||||
): Promise<void> {
|
||||
const actionSet = new ActionSet(this._actionContext, action, {
|
||||
config: config,
|
||||
const hass = this._api.getHASSManager().getHASS();
|
||||
const renderedAction =
|
||||
hass && this._templateRenderer
|
||||
? this._templateRenderer.renderRecursively(hass, action, {
|
||||
conditionState: this._api.getConditionStateManager().getState(),
|
||||
triggerData: options?.triggerData,
|
||||
})
|
||||
: action;
|
||||
|
||||
const actionSet = new ActionSet(this._actionContext, renderedAction, {
|
||||
config: options?.config,
|
||||
cardID: this._api.getConfigManager().getConfig()?.card_id,
|
||||
});
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ export class AutomationsManager {
|
||||
|
||||
const runActions = async (actions: AutomationActions): Promise<void> => {
|
||||
++this._nestedAutomationExecutions;
|
||||
|
||||
if (this._nestedAutomationExecutions > MAX_NESTED_AUTOMATION_EXECUTIONS) {
|
||||
this._api.getMessageManager().setMessageIfHigherPriority({
|
||||
type: 'error',
|
||||
@@ -72,7 +73,10 @@ export class AutomationsManager {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._api.getActionsManager().executeActions(actions);
|
||||
await this._api
|
||||
.getActionsManager()
|
||||
.executeActions(actions, { triggerData: result.data });
|
||||
|
||||
--this._nestedAutomationExecutions;
|
||||
};
|
||||
runActions(actions);
|
||||
|
||||
@@ -37,6 +37,7 @@ import { MicrophoneManager } from './microphone-manager';
|
||||
import { QueryStringManager } from './query-string-manager';
|
||||
import { StatusBarItemManager } from './status-bar-item-manager';
|
||||
import { StyleManager } from './style-manager';
|
||||
import { TemplateRenderer } from './templates';
|
||||
import { TriggersManager } from './triggers-manager';
|
||||
import {
|
||||
CardActionsManagerAPI,
|
||||
@@ -104,7 +105,7 @@ export class CardController
|
||||
);
|
||||
protected _resolvedMediaCache = new ResolvedMediaCache();
|
||||
|
||||
protected _actionsManager = new ActionsManager(this);
|
||||
protected _actionsManager = new ActionsManager(this, new TemplateRenderer());
|
||||
protected _automationsManager = new AutomationsManager(this);
|
||||
protected _cameraManager = new CameraManager(this);
|
||||
protected _cameraURLManager = new CameraURLManager(this);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { HomeAssistant } from '@dermotduffy/custom-card-helpers';
|
||||
import { HASS, renderTemplate } from 'ha-nunjucks/dist';
|
||||
import { ConditionsEvaluationData, ConditionState } from '../../conditions/types';
|
||||
import { ActionType } from '../../config/types';
|
||||
|
||||
interface TemplateContextInternal {
|
||||
camera?: string;
|
||||
view?: string;
|
||||
trigger?: ConditionsEvaluationData;
|
||||
}
|
||||
|
||||
interface TemplateContext {
|
||||
advanced_camera_card: TemplateContextInternal;
|
||||
|
||||
// Convenient alias.
|
||||
acc: TemplateContextInternal;
|
||||
}
|
||||
|
||||
export class TemplateRenderer {
|
||||
public renderRecursively = (
|
||||
hass: HomeAssistant,
|
||||
data: unknown,
|
||||
options?: {
|
||||
conditionState?: ConditionState;
|
||||
triggerData?: ConditionsEvaluationData;
|
||||
},
|
||||
): ActionType => {
|
||||
return this._renderTemplateRecursively(
|
||||
hass,
|
||||
data,
|
||||
this._conditionStateToTemplateContext(
|
||||
options?.conditionState,
|
||||
options?.triggerData,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
protected _conditionStateToTemplateContext(
|
||||
conditionState?: ConditionState,
|
||||
triggerData?: ConditionsEvaluationData,
|
||||
): TemplateContext | undefined {
|
||||
if (!conditionState?.camera && !conditionState?.view && !triggerData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const advancedCameraCardContext: TemplateContextInternal = {
|
||||
...(conditionState?.camera && { camera: conditionState.camera }),
|
||||
...(conditionState?.view && { view: conditionState.view }),
|
||||
...(triggerData && { trigger: triggerData }),
|
||||
};
|
||||
|
||||
return {
|
||||
acc: advancedCameraCardContext,
|
||||
advanced_camera_card: advancedCameraCardContext,
|
||||
};
|
||||
}
|
||||
|
||||
protected _renderTemplateRecursively(
|
||||
hass: HomeAssistant,
|
||||
data: unknown,
|
||||
templateContext?: TemplateContext,
|
||||
): ActionType {
|
||||
if (typeof data === 'string') {
|
||||
// ha-nunjucks has a more complete model of the Home Assistant object, but
|
||||
// does not export it as a type.
|
||||
return renderTemplate(hass as unknown as typeof HASS, data, templateContext);
|
||||
} else if (Array.isArray(data)) {
|
||||
return data.map((item) =>
|
||||
this._renderTemplateRecursively(hass, item, templateContext),
|
||||
);
|
||||
} else if (typeof data === 'object' && data !== null) {
|
||||
const result = {};
|
||||
for (const key in data) {
|
||||
result[key] = this._renderTemplateRecursively(hass, data[key], templateContext);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user