CwAlarm scheduler

Non-visual alarm control for TypeScript app logic. Pass a datetime plus an alarm offset, then run a callback when due.

Live demo

These buttons use the same API you will call in your app context or TypeScript modules.

Runtime state

Active alarms: 0

Total fired callbacks: 0

Last event: No alarms fired yet.

  • No events yet.

Copy-paste starter (single file)

Start here if your alarms live inside one component or one TypeScript module.

CwAlarm starter example Copy code
<script lang="ts">
	import { onDestroy } from 'svelte';
	import { CwButton, createCwAlarmScheduler } from '@cropwatchdevelopment/cwui';

	// 1) Create one scheduler instance for this component/module.
	//    It can efficiently manage many alarms with one internal timer.
	const alarms = createCwAlarmScheduler();

	// 2) Local state for UI feedback.
	let activeCount = $state(0);
	let status = $state('No alarm fired yet.');

	function scheduleInSeconds(seconds: number, label: string) {
		// 3) Schedule an alarm from "now" + offset.
		//    "from" accepts Date | ISO string | epoch milliseconds.
		alarms.schedule({
			from: Date.now(),
			alarmAfterMs: seconds * 1000,
			callback: () => {
				status = label + ' fired at ' + new Date().toLocaleTimeString();
				activeCount = alarms.size;
			}
		});

		activeCount = alarms.size;
	}

	function scheduleFromTimestamp() {
		// Optional "duration style" usage like CwDuration:
		// alarm = from + alarmAfterMinutes
		alarms.schedule({
			from: new Date('2026-02-23T12:00:00Z'),
			alarmAfterMinutes: 15,
			callback: () => {
				status = '15-minute alarm fired';
				activeCount = alarms.size;
			}
		});

		activeCount = alarms.size;
	}

	function clearAlarms() {
		alarms.clear();
		activeCount = alarms.size;
	}

	// 4) Always clean up on destroy so no timers remain active.
	onDestroy(() => {
		alarms.clear();
	});
</script>

<CwButton onclick={() => scheduleInSeconds(10, 'Irrigation check')}>
	Alarm in 10s
</CwButton>
<CwButton variant="secondary" onclick={scheduleFromTimestamp}>
	From timestamp (+15m)
</CwButton>
<CwButton variant="danger" onclick={clearAlarms}>
	Clear alarms
</CwButton>

<p>{status}</p>
<p>Active alarms: {activeCount}</p>

App context setup (recommended for apps)

Use this pattern when many screens/components need one shared scheduler instance.

CwAlarm app-context example Copy code
// src/routes/+layout.svelte
<script lang="ts">
	import { createCwAlarmContext } from '@cropwatchdevelopment/cwui';

	let { children } = $props();

	// Create once at the root so all child routes/components can share it.
	createCwAlarmContext();
</script>

{@render children()}

// src/routes/devices/+page.svelte
<script lang="ts">
	import { CwButton, useCwAlarm } from '@cropwatchdevelopment/cwui';

	const alarms = useCwAlarm();

	function watchDevice(deviceId: string) {
		// Stable id means re-scheduling replaces the previous alarm for this device.
		alarms.schedule({
			id: 'device-' + deviceId,
			from: Date.now(),
			alarmAfterMinutes: 30,
			callback: () => {
				notifyOperator(`${deviceId} has been silent for 30 minutes`);
			}
		});
	}
</script>

<CwButton onclick={() => watchDevice('sensor-12')}>
	Start 30-minute watchdog
</CwButton>
API

Usage and API reference

The alarm scheduler runs callbacks at a future time using a single timer and a priority queue, so hundreds of device timeouts cost one `setTimeout`. When a background tab becomes visible again, alarms that came due are fired and the timer is re-armed.

How to think about it

  1. Create it once Call `createCwAlarmContext()` in a root layout to share one scheduler through context, then `useCwAlarm()` in children. `createCwAlarmScheduler()` gives a standalone instance.
  2. Schedule relative to a timestamp Pass `from` (for example a device’s last-seen time) plus `alarmAfterMinutes` or `alarmAfterMs`. Reusing an `id` replaces the existing alarm, which makes “reset on new data” a single call.
  3. Cancel what you no longer need Call `cancel(id)` when a component unmounts, or `clear()` to drop everything.

Scheduler API

APITypeDetails
schedule(options)
(options: CwAlarmRegistration) => stringSchedules an alarm and returns its id.
options.from Required
Date | string | numberBase timestamp for the delay.
options.alarmAfterMinutes
numberDelay after `from`, in minutes.
options.alarmAfterMs
numberDelay after `from`, in milliseconds. Wins over minutes.
options.callback Required
() => voidRuns when the alarm fires.
options.id
stringStable id; reusing it replaces the previous alarm.
cancel(id)
(id: string) => voidCancels one alarm.
clear()
() => voidCancels every alarm.
size
numberNumber of alarms still scheduled (read-only).

Copy-paste examples

Each snippet is a complete, working example you can paste into a Svelte 5 component.

Mark a device offline after its timeout
Mark a device offline after its timeout Copy code
<script lang="ts">
	import { useCwAlarm } from '@cropwatchdevelopment/cwui';

	let { device } = $props();
	let offline = $state(false);
	const alarm = useCwAlarm();

	$effect(() => {
		offline = false;
		const id = alarm.schedule({
			id: `device-${device.id}`,
			from: device.lastSeen,
			alarmAfterMinutes: device.uploadIntervalMinutes * 2,
			callback: () => (offline = true)
		});
		return () => alarm.cancel(id);
	});
</script>