You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

254 lines
6.3 KiB

import { toEffectRaw, type Effect, type EffectRaw, fromEffectRaw } from "./assignment"
export default interface Script {
name: string
lines: ScriptLine[]
}
export interface Trigger {
id: string
name: string
event: "Button" | "Time"
deviceMatch: string
parameter: string
scriptTarget: string
scriptName: string
scriptPre: ScriptLine[]
scriptPost: ScriptLine[]
}
export interface TriggerInput extends Pick<Trigger, Exclude<keyof Trigger, "id">> {
id: string | undefined
}
export interface TriggerEditable {
id: string | null
name: string
event: "Button" | "Time"
deviceMatch: string
time: string
button: string
scriptTarget: string
scriptName: string
scriptPre: ScriptLineEditable[]
scriptPost: ScriptLineEditable[]
}
type ScriptLineEditableData = ScriptLineIf & ScriptLineAssign & ScriptLineSet & ScriptLineSelect
export interface ScriptLineEditable {
kind: "if" | "assign" | "set" | "select"
condition: Required<ScriptCondition>
effect: EffectRaw
matchKind: "all" | "name" | "role" | "raw"
matchValue: string
scope: string
key: string
value: string
then: ScriptLineEditable[]
else: ScriptLineEditable[]
}
export type ScriptLine =
| { if: ScriptLineIf }
| { assign: ScriptLineAssign }
| { set: ScriptLineSet }
| { select: ScriptLineSelect }
export interface ScriptLineIf {
condition: ScriptCondition
then: ScriptLine[]
else: ScriptLine[]
}
export interface ScriptCondition {
scope: string
key: string
op: string
value?: string
not?: boolean
}
export interface ScriptLineAssign {
match: string
effect: Effect
}
export interface ScriptLineSet {
scope: string
key: string
value: string
}
export interface ScriptLineAssign {
match: string
effect: Effect
}
export interface ScriptLineSelect {
match: string
then: ScriptLine[]
}
export function fromEditableScriptLine(line: ScriptLineEditable): ScriptLine {
switch (line.kind) {
case "if": return {
if: {
condition: line.condition,
then: line.then.map(fromEditableScriptLine),
else: line.else.map(fromEditableScriptLine),
}
};
case "assign": return {
assign: {
match: fromMatchKindValue(line),
effect: fromEffectRaw(line.effect),
}
}
case "select": return {
select: {
match: fromMatchKindValue(line),
then: line.then.map(fromEditableScriptLine),
}
}
case "set": return {
set: {
key: line.key,
scope: line.scope,
value: line.value,
}
}
}
}
export function newEditableScriptLine(): ScriptLineEditable {
return toEditableScriptLine({if: {
condition: { scope: "global", key: "", op: "eq" },
then: [],
else: [],
}})
}
export function toEditableScriptLine(line: ScriptLine): ScriptLineEditable {
const base = emptyEditable();
if ("assign" in line) {
base.kind = "assign";
base.effect = toEffectRaw(line.assign.effect);
Object.assign(base, toMatchKindValue(line.assign.match));
} else if ("if" in line) {
base.kind = "if";
base.condition = {
...line.if.condition,
not: line.if.condition.not || false,
value: line.if.condition.value || "",
};
base.then = (line.if.then||[]).map(toEditableScriptLine);
base.else = (line.if.else||[]).map(toEditableScriptLine);
} else if ("select" in line) {
base.kind = "select";
Object.assign(base, toMatchKindValue(line.select.match));
base.then = (line.select.then||[]).map(toEditableScriptLine);
} else if ("set" in line) {
base.kind = "set";
base.key = line.set.key
base.scope = line.set.scope
base.value = line.set.value
}
return base;
}
function toMatchKindValue(match: string): { matchKind: "all" | "name" | "role" | "raw", matchValue: string } {
if (match.startsWith("lucifer:name:")) {
return { matchKind: "name", matchValue: match.slice("lucifer:name:".length) };
} else if (match.startsWith("lucifer:role:")) {
return { matchKind: "role", matchValue: match.slice("lucifer:role:".length) };
} else if (match === "*") {
return { matchKind: "all", matchValue: "*" };
} else {
return { matchKind: "raw", matchValue: match }
}
}
function fromMatchKindValue({matchKind, matchValue }: { matchKind: "all" | "name" | "role" | "raw", matchValue: string }): string {
switch (matchKind) {
case "name": return `lucifer:name:${matchValue}`;
case "role": return `lucifer:role:${matchValue}`;
case "all": return "*";
case "raw": return matchValue;
}
}
function emptyEditable(): ScriptLineEditable {
return {
condition: {
key: "",
op: "add",
scope: "global",
not: false,
value: "",
},
effect: toEffectRaw({
solid: {
states: [
{color: null, intensity: null, power: null, temperature: null}
],
interleave: 0,
animationMs: 0,
}
}),
key: "",
kind: "assign",
matchKind: "name",
matchValue: "*",
scope: "match",
value: "",
then: [],
else: [],
}
}
export function newEditableTrigger(deviceMatch: string, scriptName: string): TriggerEditable {
return {
id: null,
name: "",
event: "Button",
deviceMatch: deviceMatch,
button: "On",
time: "00:00",
scriptName: scriptName,
scriptTarget: deviceMatch,
scriptPre: [],
scriptPost: [],
}
}
export function toEditableTrigger(trigger: Trigger): TriggerEditable {
return {
id: trigger.id,
name: trigger.name,
event: trigger.event,
deviceMatch: trigger.deviceMatch,
button: trigger.event === "Button" ? trigger.parameter : "Up",
time: trigger.event === "Time" ? trigger.parameter : "00:00",
scriptName: trigger.scriptName,
scriptTarget: trigger.scriptTarget,
scriptPre: trigger.scriptPre.map(toEditableScriptLine),
scriptPost: trigger.scriptPost.map(toEditableScriptLine),
}
}
export function fromEditableTrigger(editable: TriggerEditable): TriggerInput {
return {
id: editable.id || undefined,
name: editable.name,
event: editable.event,
deviceMatch: editable.deviceMatch,
parameter: (editable.event === "Button") ? editable.button : editable.time,
scriptTarget: editable.scriptTarget,
scriptName: editable.scriptName,
scriptPre: editable.scriptPre.map(fromEditableScriptLine),
scriptPost: editable.scriptPost.map(fromEditableScriptLine),
}
}