187 lines
7.1 KiB
C
187 lines
7.1 KiB
C
#include "tools/tool_cron.h"
|
|
#include "cron/cron_service.h"
|
|
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include "esp_log.h"
|
|
#include "cJSON.h"
|
|
|
|
static const char *TAG = "tool_cron";
|
|
|
|
/* ── cron_add ─────────────────────────────────────────────────── */
|
|
|
|
esp_err_t tool_cron_add_execute(const char *input_json, char *output, size_t output_size)
|
|
{
|
|
cJSON *root = cJSON_Parse(input_json);
|
|
if (!root) {
|
|
snprintf(output, output_size, "Error: invalid JSON input");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
const char *name = cJSON_GetStringValue(cJSON_GetObjectItem(root, "name"));
|
|
const char *schedule_type = cJSON_GetStringValue(cJSON_GetObjectItem(root, "schedule_type"));
|
|
const char *message = cJSON_GetStringValue(cJSON_GetObjectItem(root, "message"));
|
|
|
|
if (!name || !schedule_type || !message) {
|
|
snprintf(output, output_size, "Error: missing required fields (name, schedule_type, message)");
|
|
cJSON_Delete(root);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if (strlen(message) == 0) {
|
|
snprintf(output, output_size, "Error: message must not be empty");
|
|
cJSON_Delete(root);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
cron_job_t job;
|
|
memset(&job, 0, sizeof(job));
|
|
strncpy(job.name, name, sizeof(job.name) - 1);
|
|
strncpy(job.message, message, sizeof(job.message) - 1);
|
|
|
|
/* Optional channel and chat_id */
|
|
const char *channel = cJSON_GetStringValue(cJSON_GetObjectItem(root, "channel"));
|
|
const char *chat_id = cJSON_GetStringValue(cJSON_GetObjectItem(root, "chat_id"));
|
|
if (channel) strncpy(job.channel, channel, sizeof(job.channel) - 1);
|
|
if (chat_id) strncpy(job.chat_id, chat_id, sizeof(job.chat_id) - 1);
|
|
|
|
if (strcmp(schedule_type, "every") == 0) {
|
|
job.kind = CRON_KIND_EVERY;
|
|
cJSON *interval = cJSON_GetObjectItem(root, "interval_s");
|
|
if (!interval || !cJSON_IsNumber(interval) || interval->valuedouble <= 0) {
|
|
snprintf(output, output_size, "Error: 'every' schedule requires positive 'interval_s'");
|
|
cJSON_Delete(root);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
job.interval_s = (uint32_t)interval->valuedouble;
|
|
job.delete_after_run = false;
|
|
} else if (strcmp(schedule_type, "at") == 0) {
|
|
job.kind = CRON_KIND_AT;
|
|
cJSON *at_epoch = cJSON_GetObjectItem(root, "at_epoch");
|
|
if (!at_epoch || !cJSON_IsNumber(at_epoch)) {
|
|
snprintf(output, output_size, "Error: 'at' schedule requires 'at_epoch' (unix timestamp)");
|
|
cJSON_Delete(root);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
job.at_epoch = (int64_t)at_epoch->valuedouble;
|
|
|
|
/* Check if already in the past */
|
|
time_t now = time(NULL);
|
|
if (job.at_epoch <= now) {
|
|
snprintf(output, output_size, "Error: at_epoch %lld is in the past (now=%lld)",
|
|
(long long)job.at_epoch, (long long)now);
|
|
cJSON_Delete(root);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
/* Default: delete one-shot jobs after run */
|
|
cJSON *delete_j = cJSON_GetObjectItem(root, "delete_after_run");
|
|
job.delete_after_run = delete_j ? cJSON_IsTrue(delete_j) : true;
|
|
} else {
|
|
snprintf(output, output_size, "Error: schedule_type must be 'every' or 'at'");
|
|
cJSON_Delete(root);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
cJSON_Delete(root);
|
|
|
|
esp_err_t err = cron_add_job(&job);
|
|
if (err != ESP_OK) {
|
|
snprintf(output, output_size, "Error: failed to add job (%s)", esp_err_to_name(err));
|
|
return err;
|
|
}
|
|
|
|
/* Format success response */
|
|
if (job.kind == CRON_KIND_EVERY) {
|
|
snprintf(output, output_size,
|
|
"OK: Added recurring job '%s' (id=%s), runs every %lu seconds. Next run at epoch %lld.",
|
|
job.name, job.id, (unsigned long)job.interval_s, (long long)job.next_run);
|
|
} else {
|
|
snprintf(output, output_size,
|
|
"OK: Added one-shot job '%s' (id=%s), fires at epoch %lld.%s",
|
|
job.name, job.id, (long long)job.at_epoch,
|
|
job.delete_after_run ? " Will be deleted after firing." : "");
|
|
}
|
|
|
|
ESP_LOGI(TAG, "cron_add: %s", output);
|
|
return ESP_OK;
|
|
}
|
|
|
|
/* ── cron_list ────────────────────────────────────────────────── */
|
|
|
|
esp_err_t tool_cron_list_execute(const char *input_json, char *output, size_t output_size)
|
|
{
|
|
(void)input_json;
|
|
|
|
const cron_job_t *jobs;
|
|
int count;
|
|
cron_list_jobs(&jobs, &count);
|
|
|
|
if (count == 0) {
|
|
snprintf(output, output_size, "No cron jobs scheduled.");
|
|
return ESP_OK;
|
|
}
|
|
|
|
size_t off = 0;
|
|
off += snprintf(output + off, output_size - off,
|
|
"Scheduled jobs (%d):\n", count);
|
|
|
|
for (int i = 0; i < count && off < output_size - 1; i++) {
|
|
const cron_job_t *j = &jobs[i];
|
|
|
|
if (j->kind == CRON_KIND_EVERY) {
|
|
off += snprintf(output + off, output_size - off,
|
|
" %d. [%s] \"%s\" — every %lus, %s, next=%lld, last=%lld, ch=%s:%s\n",
|
|
i + 1, j->id, j->name,
|
|
(unsigned long)j->interval_s,
|
|
j->enabled ? "enabled" : "disabled",
|
|
(long long)j->next_run, (long long)j->last_run,
|
|
j->channel, j->chat_id);
|
|
} else {
|
|
off += snprintf(output + off, output_size - off,
|
|
" %d. [%s] \"%s\" — at %lld, %s, last=%lld, ch=%s:%s%s\n",
|
|
i + 1, j->id, j->name,
|
|
(long long)j->at_epoch,
|
|
j->enabled ? "enabled" : "disabled",
|
|
(long long)j->last_run,
|
|
j->channel, j->chat_id,
|
|
j->delete_after_run ? " (auto-delete)" : "");
|
|
}
|
|
}
|
|
|
|
ESP_LOGI(TAG, "cron_list: %d jobs", count);
|
|
return ESP_OK;
|
|
}
|
|
|
|
/* ── cron_remove ──────────────────────────────────────────────── */
|
|
|
|
esp_err_t tool_cron_remove_execute(const char *input_json, char *output, size_t output_size)
|
|
{
|
|
cJSON *root = cJSON_Parse(input_json);
|
|
if (!root) {
|
|
snprintf(output, output_size, "Error: invalid JSON input");
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
const char *job_id = cJSON_GetStringValue(cJSON_GetObjectItem(root, "job_id"));
|
|
if (!job_id || strlen(job_id) == 0) {
|
|
snprintf(output, output_size, "Error: missing 'job_id' field");
|
|
cJSON_Delete(root);
|
|
return ESP_ERR_INVALID_ARG;
|
|
}
|
|
|
|
esp_err_t err = cron_remove_job(job_id);
|
|
cJSON_Delete(root);
|
|
|
|
if (err == ESP_OK) {
|
|
snprintf(output, output_size, "OK: Removed cron job %s", job_id);
|
|
} else if (err == ESP_ERR_NOT_FOUND) {
|
|
snprintf(output, output_size, "Error: job '%s' not found", job_id);
|
|
} else {
|
|
snprintf(output, output_size, "Error: failed to remove job (%s)", esp_err_to_name(err));
|
|
}
|
|
|
|
ESP_LOGI(TAG, "cron_remove: %s -> %s", job_id, esp_err_to_name(err));
|
|
return err;
|
|
}
|