Files
advanced-camera-card/src/card-controller/automations-manager.ts
T

85 lines
2.8 KiB
TypeScript

import { ConditionsManager } from '../conditions/conditions-manager.js';
import { ConditionsEvaluationResult } from '../conditions/types.js';
import { Automation, AutomationActions } from '../config/schema/automations.js';
import { localize } from '../localize/localize.js';
import { CardAutomationsAPI, TaggedAutomation } from './types.js';
const MAX_NESTED_AUTOMATION_EXECUTIONS = 10;
export class AutomationsManager {
protected _api: CardAutomationsAPI;
protected _automations = new Map<TaggedAutomation, ConditionsManager>();
// A counter to avoid infinite loops, increases every time actions are run,
// decreases every time actions are complete.
protected _nestedAutomationExecutions = 0;
constructor(api: CardAutomationsAPI) {
this._api = api;
}
public deleteAutomations(tag?: unknown) {
for (const [automation, conditionManager] of this._automations) {
if (automation.tag === tag) {
this._automations.delete(automation);
conditionManager.destroy();
}
}
}
public addAutomations(automations: TaggedAutomation[]): void {
for (const automation of automations) {
const conditionManager = new ConditionsManager(
automation.conditions,
this._api.getConditionStateManager(),
);
conditionManager.addListener((result: ConditionsEvaluationResult) =>
this._execute(automation, result),
);
this._automations.set(automation, conditionManager);
}
}
protected _execute(automation: Automation, result: ConditionsEvaluationResult): void {
if (
!this._api.getHASSManager().hasHASS() ||
// Never execute automations if the card hasn't finished initializing, as
// it could cause a view change when camera loads are not finished.
// See: https://github.com/dermotduffy/advanced-camera-card/issues/1407
!this._api.getInitializationManager().isInitializedMandatory() ||
// Never execute automations if there's an error (as our automation loop
// avoidance -- which shows as an error -- would not work!).
this._api.getMessageManager().hasErrorMessage()
) {
return;
}
const shouldExecute = result.result;
const actions = shouldExecute ? automation.actions : automation.actions_not;
if (!actions?.length) {
return;
}
const runActions = async (actions: AutomationActions): Promise<void> => {
++this._nestedAutomationExecutions;
if (this._nestedAutomationExecutions > MAX_NESTED_AUTOMATION_EXECUTIONS) {
this._api.getMessageManager().setMessageIfHigherPriority({
type: 'error',
message: localize('error.too_many_automations'),
});
return;
}
await this._api
.getActionsManager()
.executeActions({ actions, triggerData: result.triggerData });
--this._nestedAutomationExecutions;
};
runActions(actions);
}
}