Skip to content

File espos_config_desc.h

File List > espos_config > include > espos_config_desc.h

Go to the documentation of this file

/*
 * SPDX-FileCopyrightText: 2026 Dirk Wahrheit
 * SPDX-License-Identifier: Apache-2.0
 *
 * espOS config descriptor tables.
 *
 * These types are instantiated by the build-time generator
 * (tools/espos_gen_config.py) from every registered config descriptor. The
 * runtime never spells defaults, ranges or key names by hand — it consults
 * these tables.
 */
#pragma once

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef enum {
    ESPOS_CFG_TYPE_BOOL = 1,
    ESPOS_CFG_TYPE_INT,    /* int32_t */
    ESPOS_CFG_TYPE_FLOAT,  /* float, stored as its IEEE-754 bit pattern */
    ESPOS_CFG_TYPE_STRING, /* NUL-terminated UTF-8, ≤ max_len bytes excluding NUL */
    ESPOS_CFG_TYPE_BLOB,   /* opaque bytes, ≤ max_len; JSON form is base64 */
} espos_cfg_type_t;

#define ESPOS_CFG_FLAG_SECRET           (1u << 0) /* redacted on export, sentinel ignored on import */
#define ESPOS_CFG_FLAG_RESTART_REQUIRED (1u << 1) /* change takes effect after reboot */
/* Displayed but not editable: the UI greys the control out and a write is
 * rejected. A device-derived value (a serial number, a computed limit) that a
 * user must be able to read but never set. */
#define ESPOS_CFG_FLAG_READ_ONLY (1u << 2)

/* Presentation hints. They never change what is stored — only how a value is
 * shown and typed in. SI units are the on-device truth (SignalK's rule), but
 * nobody trims an anchor rode in radians, so the UI multiplies on read and
 * divides on write: display = stored * display_mul + display_off. A descriptor
 * that sets neither leaves both at their identity (1.0 / 0.0). */
typedef struct {
    const char *group;    /* UI tab, "" = the namespace's own tab */
    float display_mul;    /* 0 == unset, treated as 1 */
    float display_off;
    /* A string key whose contents are a JSON array of rows, edited as a
     * table. Columns are the JSON member names, in order; NULL = not a table.
     * The value stays a readable NVS string so an export is diffable. */
    const char *const *table_columns;
    size_t table_column_count;
} espos_cfg_display_t;

typedef union {
    bool b;
    int32_t i;
    float f;
    const char *s;
} espos_cfg_scalar_t;

typedef struct {
    const char *name;        /* NVS key, ≤ 15 chars */
    const char *title;
    const char *description;
    const char *unit;        /* "" if none */
    espos_cfg_type_t type;
    uint32_t flags;
    espos_cfg_scalar_t def;  /* compiled-in default (unused for blob) */
    espos_cfg_scalar_t min;  /* int: always valid; float: iff has_min */
    espos_cfg_scalar_t max;  /* int: always valid; float: iff has_max */
    bool has_min;
    bool has_max;
    size_t max_len;          /* string: bytes excl. NUL; blob: bytes */
    const char *const *enum_values; /* string only, may be NULL */
    size_t enum_count;
    /* Presentation only. A key that sets nothing here is all-zero, which the
     * runtime reads as "no group, identity scaling, not a table" — so every
     * descriptor written before these fields existed stays correct. */
    espos_cfg_display_t display;
} espos_cfg_key_t;

typedef struct {
    const char *name;    /* NVS namespace, ≤ 15 chars */
    const char *title;
    uint16_t version;    /* current descriptor version; stored under key "config_version" */
    const espos_cfg_key_t *keys;
    size_t key_count;
    /* Runtime namespaces only (see espos_config_register_ns): the human name
     * of what registered this, shown as the tab label's subtitle. NULL for a
     * build-time descriptor. */
    const char *description;
} espos_cfg_ns_t;

/* NVS caps both a namespace and a key name at 15 characters plus NUL. A node
 * that registers its own namespace gets the prefix below, so its id may be at
 * most 12 characters. */
#define ESPOS_CFG_RUNTIME_NS_PREFIX "f_"
#define ESPOS_CFG_RUNTIME_ID_MAX    12
#define ESPOS_CFG_NS_NAME_MAX       15

/* Emitted by the generator (sorted by namespace name). */
extern const espos_cfg_ns_t espos_cfg_namespaces[];
extern const size_t espos_cfg_namespace_count;

/* JSON Schema of the whole configuration document, generated at build time. */
extern const char espos_cfg_schema_json[];
extern const size_t espos_cfg_schema_json_len;
/* Short content hash of the schema text (usable as HTTP ETag). */
extern const char espos_cfg_schema_etag[];

/* Reserved key holding the per-namespace schema version. */
#define ESPOS_CFG_VERSION_KEY "config_version"

#ifdef __cplusplus
}
#endif