Appearance
Script Examples
Last updated
Each example says which trigger to use. Replace device names, tag paths and table names with your own; Data Tables must already exist with those column names.
Log a production row on every part (Tag Change)
Trigger: Tag Change, Tag = the CNC's part_count (chosen with Browse). Data Table production_log with columns machine, part_count, spindle_speed, feed_rate, logged_at.
javascript
const partCount = Number(context.value);
if (!(partCount > 0)) return; // ignore 0 / non-numeric resets
const speed = Number(tags.read('CNC-001', 'spindle_speed'));
const feed = Number(tags.read('CNC-001', 'feed_rate'));
db.insert('production_log', {
machine: 'CNC-001',
part_count: partCount,
spindle_speed: speed,
feed_rate: feed,
logged_at: context.timestamp,
});
log('Logged part #' + partCount + ' at ' + speed + ' RPM');Coolant alert with a latch (Tag Threshold)
Trigger: Tag Threshold, Operator = Greater Than, Threshold = 60, on the coolant temperature tag. Because Tag Threshold runs on every update above 60, a local tag CNC001/coolant_alert remembers that the alert was already sent. A second script (or an alarm rule) resets it when the temperature falls. (Ticking Only when it crosses, with a Hysteresis of 3, gives the same once-per-excursion behavior without the latch tag; the latch is still useful when other screens need to show the alert.)
javascript
const temp = context.value; // a number for Tag Threshold
if (tags.read(devices.localId(), 'CNC001/coolant_alert') === 'true') return; // already sent
tags.write('CNC001/coolant_alert', 'true');
log('Coolant temperature ' + temp + ' °C');
const reply = await http.post('https://hooks.example.com/alert', {
machine: 'CNC-001',
temperature: temp,
message: 'Coolant temperature exceeded 60 °C',
});
log('Webhook replied', reply); // throws if the webhook answers with an error statusTo raise an alarm operators see on the Alarms page, add alarms.notify('warning', 'Coolant hot', 'Coolant at ' + temp + ' °C'). For an email or text as well, create an alarm rule on CNC001/coolant_alert = true with a pipeline.
Cycle time from a part counter (Interval)
Trigger: Interval, 1000 ms. Local tags CNC001/last_part_count, CNC001/last_part_time and CNC001/cycle_time should exist so they survive a restart.
javascript
const local = devices.localId();
const partCount = Number(tags.read('CNC-001', 'part_count'));
const lastCount = Number(tags.read(local, 'CNC001/last_part_count') || '0');
if (!(partCount > lastCount)) return;
const now = Date.now();
const lastTime = Number(tags.read(local, 'CNC001/last_part_time') || String(now));
const cycleSec = (now - lastTime) / 1000;
await tags.writeBatch([
{ tagPath: 'CNC001/last_part_count', value: partCount },
{ tagPath: 'CNC001/last_part_time', value: now },
{ tagPath: 'CNC001/cycle_time', value: cycleSec.toFixed(1) },
]);
db.insert('cycle_times', { machine: 'CNC-001', part_count: partCount, cycle_time_sec: cycleSec });
log('Part ' + partCount + ': ' + cycleSec.toFixed(1) + ' s');Hand off to a workflow (any trigger)
To start one particular workflow and wait for its result, run it by name. (messages.send() also reaches workflows, but only ones with a Message Received trigger for that name, and it doesn't wait for them.)
javascript
const result = await workflows.run('SICK Trigger Inspection', { station: 'OP20' });
if (result.status !== 'completed') {
throw new Error('Inspection workflow failed: ' + result.error); // marks this run failed
}End-of-shift job (Cron Schedule, saved by an administrator)
Trigger: Cron Schedule 0 23 * * * (23:00 appliance time). system.backup() and users.logoutAll() need the script to be saved by an administrator.
javascript
const backup = system.backup();
log('Nightly backup: ' + backup.filename + ' (' + backup.sizeBytes + ' bytes)');
for (const alarm of alarms.getActive()) {
alarms.acknowledge(alarm.id);
}
if (email.isConfigured()) {
const produced = tags.read('CNC-001', 'part_count') ?? 'unknown';
await email.send({
to: 'supervisor@plant.local',
subject: 'Shift complete — CNC-001',
text: 'Parts produced this shift: ' + produced,
});
}
users.logoutAll(); // sign out the kiosk displaysShift report from a named query (Cron Schedule)
javascript
const rows = await namedQueries.run('downtime_by_reason', {
line: 2,
start: new Date(Date.now() - 8 * 3600 * 1000).toISOString(),
});
const lines = rows.map((r) => r.reason + ': ' + Number(r.minutes).toFixed(0) + ' min');
log(lines);
if (rows.length && email.isConfigured()) {
await email.send({ to: 'line2@plant.local', subject: 'Line 2 downtime, last 8 h', text: lines.join('\n') });
}Temporary contractor account (Manual, saved by an administrator)
javascript
const contractorEmail = context.params.email; // passed from a button or the API
if (!contractorEmail) throw new Error('Pass an email parameter');
const id = users.add({
email: contractorEmail,
password: 'Temp-' + Math.random().toString(36).slice(2, 10),
firstName: 'Visiting',
lastName: 'Tech',
role: 'operator',
});
log('Created temporary operator #' + id);
// Later: users.disable(id) keeps their history; users.remove(id) deletes the account.