Skip to content

File espos_config.h

File List > espos_config > include > espos_config.h

Go to the documentation of this file

/*
 * SPDX-FileCopyrightText: 2026 Dirk Wahrheit
 * SPDX-License-Identifier: Apache-2.0
 *
 * espos_config โ€” NVS-backed, schema-described configuration store.
 *
 * Every value lives in an NVS namespace under a short key. What namespaces
 * and keys exist, their types, defaults and limits, is declared once in a
 * config descriptor (JSON) per component and turned into the tables in
 * espos_config_desc.h at build time. Use the generated espos_cfg_keys.h
 * constants rather than string literals.
 *
 * Reads never fail on missing or corrupt values: they fall back to the
 * compiled-in default. Writes are validated against the descriptor first and
 * committed per key; a JSON import validates the whole document before it
 * writes anything.
 *
 * Thread-safe: all calls take an internal mutex. Change callbacks run on the
 * caller's task with the mutex NOT held.
 */
#pragma once

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#include "espos_config_desc.h"
#include "espos_config_backend.h"

#ifdef __cplusplus
extern "C" {
#endif

/* Sentinel returned in place of secret values on export; ignored on import. */
#define ESPOS_CONFIG_SECRET_SENTINEL "********"

/* ---------------------------------------------------------------- lifecycle */

esp_err_t espos_config_init(const espos_config_backend_t *backend, void *backend_ctx);

bool espos_config_is_ready(void);

void espos_config_deinit(void);

bool espos_config_storage_was_reset(void);

esp_err_t espos_config_factory_reset(void);

/* --------------------------------------------------------------- migrations */

typedef struct espos_config_migrate_ctx espos_config_migrate_ctx_t;
typedef esp_err_t (*espos_config_migrate_fn_t)(espos_config_migrate_ctx_t *ctx, void *arg);

esp_err_t espos_config_register_migration(const char *ns, uint16_t from_version,
                                          espos_config_migrate_fn_t fn, void *arg);

/* Raw accessors usable only inside a migration callback (no validation,
 * arbitrary types โ€” a migration may read a key in its OLD type). */
esp_err_t espos_config_migrate_get(espos_config_migrate_ctx_t *ctx, const char *key,
                                   espos_cfg_type_t type, void *buf, size_t *len);
esp_err_t espos_config_migrate_set(espos_config_migrate_ctx_t *ctx, const char *key,
                                   espos_cfg_type_t type, const void *buf, size_t len);
esp_err_t espos_config_migrate_erase(espos_config_migrate_ctx_t *ctx, const char *key);
uint16_t espos_config_migrate_from_version(const espos_config_migrate_ctx_t *ctx);

esp_err_t espos_config_get_version(const char *ns, uint16_t *stored, uint16_t *current);

/* ---------------------------------------------------------------- accessors */

/* Reads: return ESP_OK with the stored value or the compiled-in default.
 * ESP_ERR_NOT_FOUND only if ns/key are not declared. */
esp_err_t espos_config_get_bool(const char *ns, const char *key, bool *out);
esp_err_t espos_config_get_i32(const char *ns, const char *key, int32_t *out);
esp_err_t espos_config_get_float(const char *ns, const char *key, float *out);
esp_err_t espos_config_get_str(const char *ns, const char *key, char *buf, size_t buf_size,
                               size_t *out_len);
esp_err_t espos_config_get_blob(const char *ns, const char *key, void *buf, size_t *len);

/* Writes: validated against the descriptor. ESP_ERR_INVALID_ARG on type/range/
 * length/enum violation, ESP_ERR_NOT_FOUND on undeclared key. Change callbacks
 * fire only if the effective value actually changed. */
esp_err_t espos_config_set_bool(const char *ns, const char *key, bool v);
esp_err_t espos_config_set_i32(const char *ns, const char *key, int32_t v);
esp_err_t espos_config_set_float(const char *ns, const char *key, float v);
esp_err_t espos_config_set_str(const char *ns, const char *key, const char *v);
esp_err_t espos_config_set_blob(const char *ns, const char *key, const void *buf, size_t len);

esp_err_t espos_config_reset_key(const char *ns, const char *key);
esp_err_t espos_config_reset_ns(const char *ns);
bool espos_config_is_set(const char *ns, const char *key);

/* --------------------------------------------------------------- descriptor */

const espos_cfg_ns_t *espos_config_find_ns(const char *ns);
const espos_cfg_key_t *espos_config_find_key(const espos_cfg_ns_t *ns, const char *key);

/* ---------------------------------------------------- runtime namespaces */

esp_err_t espos_config_register_ns(const espos_cfg_ns_t *ns);

esp_err_t espos_config_unregister_ns(const char *ns);

size_t espos_config_runtime_ns_count(void);

esp_err_t espos_config_flow_ns_name(const char *id, char *out, size_t out_size);

/* -------------------------------------------------------------- schema */

#define ESPOS_CFG_ETAG_MAX 24
esp_err_t espos_config_schema_json(char **out, char etag[ESPOS_CFG_ETAG_MAX]);

void espos_config_schema_etag(char etag[ESPOS_CFG_ETAG_MAX]);

/* -------------------------------------------------------------- change feed */

typedef void (*espos_config_change_cb_t)(const char *ns, const char *key, void *arg);
esp_err_t espos_config_subscribe(espos_config_change_cb_t cb, void *arg);
esp_err_t espos_config_unsubscribe(espos_config_change_cb_t cb, void *arg);

/* ------------------------------------------------------------------- JSON */

esp_err_t espos_config_export_json(const char *only_ns, bool include_secrets, char **out_json);

typedef struct {
    size_t changed;          /* keys whose effective value changed */
    bool restart_required;   /* at least one changed key is flagged restart_required */
    /* On validation failure: the first offending "ns.key" and a reason. */
    char error_path[40];
    char error_msg[96];
} espos_config_import_result_t;

esp_err_t espos_config_import_json(const char *json, size_t json_len, bool ignore_unknown,
                                   espos_config_import_result_t *result, char **out_report_json);

#ifdef __cplusplus
}
#endif