Skip to content

File espos_sk.h

File List > espos_sk > include > espos_sk.h

Go to the documentation of this file

/*
 * SPDX-FileCopyrightText: 2026 Dirk Wahrheit
 * SPDX-License-Identifier: Apache-2.0
 *
 * espos_sk โ€” SignalK server discovery and access-token management (M3);
 * WebSocket delta output and meta reconciliation follow in M4.
 */
#pragma once

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "esp_err.h"
#include "espos_sk_token_sm.h"

#ifdef __cplusplus
extern "C" {
#endif

/* A boat network can carry more SignalK servers than you would guess: a
 * plotter, a spare Pi, a laptop running one for development, plus every
 * neighbouring vessel in the marina. Measured on one: 9 advertisements.
 * When the list is smaller than what is advertised, which entries survive
 * depends on mDNS answer order, which is not stable โ€” so the server the
 * device is actually paired with can silently drop out. */
#define ESPOS_SK_MAX_SERVERS 12

typedef struct {
    char host[ESPOS_SK_HOST_MAX];   /* IPv4 dotted or hostname */
    uint16_t port;
    char self[ESPOS_SK_SELF_MAX];
    char name[48];                  /* mDNS instance name */
    char roles[32];
    char swname[24];
    char swvers[16];
    /* The server advertised itself as _signalk-https._tcp rather than
     * _signalk-http._tcp. signalk-server publishes one or the other depending
     * on its `ssl` setting (src/interfaces/rest.js), so this is the server
     * telling us its scheme -- which is what sk.scheme = auto reads. */
    bool tls;
    uint32_t seen_ms;
} espos_sk_discovered_t;

esp_err_t espos_sk_start(void);
esp_err_t espos_sk_stop(void);

esp_err_t espos_sk_status_json(char **out_json);
esp_err_t espos_sk_servers_json(char **out_json);

/* Commands (thread-safe, queued to the SK task). */
esp_err_t espos_sk_discover_now(void);
esp_err_t espos_sk_request_now(void);            /* re-request access (from denied/error) */
esp_err_t espos_sk_set_token(const char *token); /* manual token paste */
esp_err_t espos_sk_forget_token(void);           /* drop token + pending, request again */
void espos_sk_report_unauthorized(void);
void espos_sk_report_cert_error(const char *reason);

/* ------------------------------------------------------------ deltas */

esp_err_t espos_sk_publish_number(const char *path, double value);
esp_err_t espos_sk_publish_string(const char *path, const char *value);
esp_err_t espos_sk_publish_bool(const char *path, bool value);
esp_err_t espos_sk_publish_json(const char *path, const char *value_json);

typedef enum {
    ESPOS_SK_ALERT_NORMAL = 0,  /* condition cleared */
    ESPOS_SK_ALERT_WARN,
    ESPOS_SK_ALERT_ALARM,
} espos_sk_alert_t;

esp_err_t espos_sk_notify(const char *key, espos_sk_alert_t state, const char *message);

/* The compile-time cap. CONFIG_ESPOS_SK_MAX_META tunes the table the
 * implementation actually allocates; this is the number the API promises and
 * stays a literal, because a public header that reads a CONFIG_ token gives
 * two firmwares built from one header different ABIs. */
#define ESPOS_SK_MAX_META 32
esp_err_t espos_sk_declare_meta(const char *path, const char *meta_json, uint32_t period_ms);

/* ------------------------------------------------------- inbound (M7) */

#include "espos_sk_parse.h"

typedef void (*espos_sk_sub_cb_t)(const espos_sk_update_t *u, void *arg);
#define ESPOS_SK_MAX_SUBS 48
int espos_sk_subscribe(const char *pattern, uint32_t period_ms, espos_sk_sub_cb_t cb, void *arg);
esp_err_t espos_sk_unsubscribe(int handle);

typedef void (*espos_sk_put_cb_t)(const char *request_id, const char *state, int status_code, const char *message, void *arg);
esp_err_t espos_sk_put(const char *path, const char *value_json, espos_sk_put_cb_t cb, void *arg);

esp_err_t espos_sk_send_raw(const char *json);

/* -------------------------------------------------- inbound PUT (control) */

typedef esp_err_t (*espos_sk_put_handler_t)(const char *path, const char *value_json, void *arg);

#define ESPOS_SK_PUT_PENDING 1

#define ESPOS_SK_MAX_PUT_HANDLERS 16
esp_err_t espos_sk_put_handler_register(const char *path, espos_sk_put_handler_t cb, void *arg);
esp_err_t espos_sk_put_handler_unregister(const char *path);

esp_err_t espos_sk_put_respond(const char *request_id, const char *state, int status_code, const char *message);

esp_err_t espos_sk_flush(uint32_t timeout_ms);

typedef struct {
    bool enabled;
    bool connected;
    uint32_t connected_s;
    uint32_t reconnects;      /* successful connections so far */
    uint32_t sent;            /* messages sent */
    uint32_t send_errors;
    uint32_t next_retry_s;    /* while disconnected */
    char last_error[64];
    size_t pending, buffered, buffered_bytes;
    uint32_t dropped;
    size_t meta_declared, meta_reconciled;
    uint32_t received;        /* value/meta items delivered to subscribers */
    uint32_t frames;          /* text frames read */
    size_t subs;              /* active subscriptions */
    size_t puts_pending;
    uint32_t puts_sent, puts_failed;
    uint32_t puts_in;         /* inbound PUT items received from the server */
    uint32_t puts_rejected;   /* of those, answered 405 (no handler) or 502 */
} espos_sk_ws_status_t;
esp_err_t espos_sk_ws_get_status(espos_sk_ws_status_t *out);

esp_err_t espos_sk_get_token(char *buf, size_t size);
esp_err_t espos_sk_get_server(espos_sk_server_t *out);
const char *espos_sk_client_id(void);

esp_err_t espos_sk_set_app_name(const char *name);

#ifdef __cplusplus
}
#endif