Automation should use event store for data (#1962)

* fix: automation should use RuntimeStore for data

* add: aux timer template help
This commit is contained in:
Alex Christoffer Rasmussen
2026-03-02 11:54:46 -08:00
committed by GitHub
parent c1fcdf7065
commit 358ad79ae4
7 changed files with 87 additions and 56 deletions
@@ -3,6 +3,7 @@ import {
isOntimeAction,
isOSCOutput,
LogOrigin,
RuntimeStore,
TimerLifeCycle,
type AutomationFilter,
type AutomationOutput,
@@ -10,29 +11,30 @@ import {
} from 'ontime-types';
import { getPropertyFromPath } from 'ontime-utils';
import { logger } from '../../classes/Logger.js';
import { getState, type RuntimeState } from '../../stores/runtimeState.js';
import { isOntimeCloud } from '../../setup/environment.js';
import { emitOSC } from './clients/osc.client.js';
import { emitHTTP } from './clients/http.client.js';
import { getAutomationsEnabled, getAutomations, getAutomationTriggers } from './automation.dao.js';
import { isContained, isEquivalent, isGreaterThan, isLessThan } from './automation.utils.js';
import { toOntimeAction } from './clients/ontime.client.js';
import { logger } from '../../classes/Logger.js';
import { isOntimeCloud } from '../../setup/environment.js';
import { eventStore } from '../../stores/EventStore.js';
/**
* Exposes a method for triggering actions based on a TimerLifeCycle event
*/
export function triggerAutomations(cycle: TimerLifeCycle, state: RuntimeState) {
export function triggerAutomations(cycle: TimerLifeCycle) {
if (!getAutomationsEnabled()) {
return;
}
const store = eventStore.poll();
let triggers = getAutomationTriggers();
// get triggers from event
if (state.eventNow?.triggers) {
triggers = triggers.concat(state.eventNow.triggers);
if (store.eventNow?.triggers) {
triggers = triggers.concat(store.eventNow.triggers);
}
// note: there are no onStop triggers in event
@@ -51,9 +53,9 @@ export function triggerAutomations(cycle: TimerLifeCycle, state: RuntimeState) {
if (!automation || automation.outputs.length === 0) {
return;
}
const shouldSend = testConditions(automation.filters, automation.filterRule, state);
const shouldSend = testConditions(automation.filters, automation.filterRule, store);
if (shouldSend) {
send(automation.outputs, state);
send(automation.outputs, store);
}
});
}
@@ -62,7 +64,8 @@ export function triggerAutomations(cycle: TimerLifeCycle, state: RuntimeState) {
* Exposes a method for bypassing the condition check and testing the sending of an output
*/
export function testOutput(payload: AutomationOutput) {
send([payload]);
const store = eventStore.poll();
send([payload], store);
}
/**
@@ -71,7 +74,7 @@ export function testOutput(payload: AutomationOutput) {
export function testConditions(
filters: AutomationFilter[],
filterRule: FilterRule,
state: Partial<RuntimeState>,
store: Partial<RuntimeStore>,
): boolean {
if (filters.length === 0) {
return true;
@@ -86,7 +89,7 @@ export function testConditions(
function evaluateCondition(filter: AutomationFilter): boolean {
const { field, operator, value } = filter;
const lowerCasedValue = value.toLowerCase();
const fieldValue = getPropertyFromPath(field, state);
const fieldValue = getPropertyFromPath(field, store);
// if value is empty string, the user could be meaning to check if the value does not exist
// we use loose equality to be able to check for converted values (eg '10' == 10)
@@ -115,13 +118,12 @@ export function testConditions(
* Handles preparing and sending of the data
* Returns a boolean indicating whether a message was sent
*/
function send(output: AutomationOutput[], state?: RuntimeState) {
const stateSnapshot = state ?? getState();
function send(output: AutomationOutput[], store: RuntimeStore) {
output.forEach((payload) => {
if (isOSCOutput(payload) && !isOntimeCloud) {
emitOSC(payload, stateSnapshot);
emitOSC(payload, store);
} else if (isHTTPOutput(payload)) {
emitHTTP(payload, stateSnapshot);
emitHTTP(payload, store);
} else if (isOntimeAction(payload)) {
toOntimeAction(payload);
} else {