108 lines
3.4 KiB
C
108 lines
3.4 KiB
C
// See routine_manifest.h. The pure parser is host-testable (no ESP-IDF deps);
|
|
// the SD loader is compiled only on-target (#ifndef LISAEL_HOST_TEST).
|
|
#include "audio/routine_manifest.h"
|
|
#include "cJSON.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
int lisael_routine_parse(const char *json, lisael_routine_t *routines, int max_routines)
|
|
{
|
|
if (!json || !routines || max_routines <= 0) {
|
|
return -1;
|
|
}
|
|
cJSON *root = cJSON_Parse(json);
|
|
if (!cJSON_IsObject(root)) {
|
|
cJSON_Delete(root);
|
|
return -1;
|
|
}
|
|
cJSON *arr = cJSON_GetObjectItem(root, "routines");
|
|
if (!cJSON_IsArray(arr)) {
|
|
cJSON_Delete(root);
|
|
return -1;
|
|
}
|
|
|
|
int n_routines = 0;
|
|
cJSON *jr;
|
|
cJSON_ArrayForEach(jr, arr) {
|
|
if (n_routines >= max_routines) {
|
|
break;
|
|
}
|
|
cJSON *jid = cJSON_GetObjectItem(jr, "id");
|
|
cJSON *jtitle = cJSON_GetObjectItem(jr, "title");
|
|
cJSON *jicon = cJSON_GetObjectItem(jr, "icon");
|
|
cJSON *jsteps = cJSON_GetObjectItem(jr, "steps");
|
|
if (!cJSON_IsString(jid) || !cJSON_IsArray(jsteps)) {
|
|
continue;
|
|
}
|
|
lisael_routine_t *r = &routines[n_routines];
|
|
memset(r, 0, sizeof(*r));
|
|
snprintf(r->id, sizeof(r->id), "%s", jid->valuestring);
|
|
if (cJSON_IsString(jtitle)) {
|
|
snprintf(r->title, sizeof(r->title), "%s", jtitle->valuestring);
|
|
} else {
|
|
snprintf(r->title, sizeof(r->title), "%s", jid->valuestring);
|
|
}
|
|
if (cJSON_IsString(jicon)) {
|
|
snprintf(r->icon, sizeof(r->icon), "%s", jicon->valuestring);
|
|
}
|
|
cJSON *jstep;
|
|
cJSON_ArrayForEach(jstep, jsteps) {
|
|
if (r->n_steps >= LISAEL_MAX_STEPS) {
|
|
break;
|
|
}
|
|
cJSON *jtext = cJSON_GetObjectItem(jstep, "text");
|
|
cJSON *jsicon = cJSON_GetObjectItem(jstep, "icon");
|
|
cJSON *jaudio = cJSON_GetObjectItem(jstep, "audio");
|
|
if (!cJSON_IsString(jtext)) {
|
|
continue; // a step with no label is useless; skip it
|
|
}
|
|
lisael_step_t *s = &r->steps[r->n_steps];
|
|
snprintf(s->text, sizeof(s->text), "%s", jtext->valuestring);
|
|
if (cJSON_IsString(jsicon)) {
|
|
snprintf(s->icon, sizeof(s->icon), "%s", jsicon->valuestring);
|
|
}
|
|
if (cJSON_IsString(jaudio)) {
|
|
snprintf(s->audio, sizeof(s->audio), "%s", jaudio->valuestring);
|
|
}
|
|
r->n_steps++;
|
|
}
|
|
if (r->n_steps > 0) {
|
|
n_routines++; // skip routines with no usable step
|
|
}
|
|
}
|
|
cJSON_Delete(root);
|
|
return n_routines;
|
|
}
|
|
|
|
#ifndef LISAEL_HOST_TEST
|
|
#include "esp_log.h"
|
|
int lisael_routine_load(lisael_routine_t *routines, int max_routines)
|
|
{
|
|
const char *path = "/sdcard/routines/routines.json";
|
|
FILE *f = fopen(path, "r");
|
|
if (!f) {
|
|
return -1;
|
|
}
|
|
fseek(f, 0, SEEK_END);
|
|
long sz = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
if (sz <= 0 || sz > 64 * 1024) {
|
|
fclose(f);
|
|
return -1;
|
|
}
|
|
char *buf = malloc((size_t)sz + 1);
|
|
if (!buf) {
|
|
fclose(f);
|
|
return -1;
|
|
}
|
|
size_t rd = fread(buf, 1, (size_t)sz, f);
|
|
buf[rd] = '\0';
|
|
fclose(f);
|
|
int n = lisael_routine_parse(buf, routines, max_routines);
|
|
free(buf);
|
|
ESP_LOGI("routine", "loaded %d routine(s) from %s", n, path);
|
|
return n;
|
|
}
|
|
#endif
|