MeshDash Docs
R2.0
/
Home Getting Started Task Scheduler

Task Scheduler

Getting Started task scheduler cron croniter heartbeat community api retry send message website monitor scheduler internals
How task_scheduler.py evaluates cron expressions, executes tasks, sends heartbeats, and handles retries.

task_scheduler.py runs as a background asyncio coroutine (run_scheduler_periodically()) within the main MeshDash process. It has two responsibilities: executing scheduled tasks and sending community heartbeats.

Execution Loop

The scheduler loop runs every 60 seconds. On each cycle it:

  1. Reads all tasks from tasks.db
  2. For each enabled task, uses croniter to determine whether the task was due since the last check
  3. Executes any due tasks via HTTP POST to the local MeshDash API
  4. Sends a community heartbeat if enabled and the interval has elapsed

Cron Evaluation

The scheduler uses the croniter library with the system's local timezone (detected via tzlocal). This means cron expressions like 0 8 * * * fire at 8 AM in the system's local time, not UTC.

The evaluation window is: did a trigger fall between the last check time and now? This is robust to scheduler restarts — if the service was down when a task was due, it will run on the next cycle after restart (within the evaluation window).

Task Types

Standard Message (taskType: message)

Payload is a plain text string. The scheduler POSTs:

POST /api/messages
{"message": "Good morning!", "destination": "!aabbccdd"}

If the actionPayload is valid JSON with a message key, it is sent as-is (allowing channel overrides). If it is plain text, it is wrapped into the above structure.

Web Sensor / Website Monitor (taskType: website_monitor)

Payload is a JSON string. When the scheduler detects url, block_id, and prefix keys in the payload, it calls the web monitor endpoint instead:

POST /monitor/website
{
  "url": "https://example.com/data",
  "block_id": 3,
  "prefix": "Tank Level:",
  "node_id": "!aabbccdd",
  "channel": 0
}

The server fetches the URL, extracts the specified block, prepends the prefix, and sends the result as a Meshtastic message. Accepted taskType values: message, send, sendmessage, sendmsg, website_monitor, websitemonitor — all are treated identically.

Retry Logic

Each task action retries up to SCHEDULER_MAX_RETRIES times (default 3) on failure. Between attempts, the scheduler waits SCHEDULER_RETRY_DELAY_SECONDS (default 10 s). Retries apply to:

  • HTTP timeout errors (both connect and read/write)
  • HTTP 5xx server errors from the local API
  • Network request errors

Non-retriable failures (HTTP 4xx) fail immediately without retrying.

Community Heartbeat

If COMMUNITY_API=true, the scheduler sends periodic heartbeats to meshdash.co.uk containing:

  • Node status and statistics
  • Local node GPS position (if SEND_LOCAL_NODE_LOCATION=true)
  • Other visible node positions (if SEND_OTHER_NODES_LOCATION=true)
  • Location offset applied if LOCATION_OFFSET_ENABLED=true

Heartbeat frequency is controlled by HEARTBEAT_INTERVAL_MINUTES (default: 1 minute).

The C2 remote access bridge (remote_c2_worker_enhanced()) and the community heartbeat in the task scheduler both communicate with meshdash.co.uk, but they are separate workers with different purposes. The C2 bridge handles proxy requests; the scheduler heartbeat updates the community map.

Timezone Handling

The scheduler requires tzlocal to be installed for accurate local time handling. If missing, it falls back to the system's UTC offset. Install with:

pip install tzlocal

Check your system timezone with timedatectl (Linux) to verify cron expressions will fire at the expected local time.

Scheduler Configuration Tuning

The scheduler reads several config keys that affect its behaviour at runtime:

SCHEDULER_MAX_RETRIES
How many times to retry a failed task action. Default: 3. Set to 1 to disable retries for time-sensitive tasks.
SCHEDULER_RETRY_DELAY_SECONDS
Wait between retry attempts. Default: 10 s. Increase if your radio intermittently rejects packets.
SCHEDULER_CONNECT_TIMEOUT
HTTP connection timeout for API calls the scheduler makes. Default: 10.0 s.
SCHEDULER_RW_TIMEOUT
Read/write timeout for API calls. Default: 30.0 s. Must be long enough for the radio to accept the packet.
HEARTBEAT_INTERVAL_MINUTES
Community map update frequency. Default: 1 minute. Increase to reduce external requests.

Task Status Tracking

The scheduler does not update task status back to the database — it simply fires and forgets with retry logic. If a task needs to confirm delivery, use the MeshShark view to observe ACK packets, or check the GET /api/messages/history endpoint for the DELIVERED status after the task fires.

Task Enabled Flag

Tasks with enabled: false are fetched from the database but skipped immediately during evaluation. This allows you to pause individual tasks without deleting them. The Tasks view in the dashboard shows an enabled/disabled toggle on each task row.