SensESP 3.3.0
Universal Signal K sensor toolkit ESP32
Loading...
Searching...
No Matches
signalk_ws_client.h
Go to the documentation of this file.
1#ifndef SENSESP_SRC_SENSESP_SIGNALK_SIGNALK_WS_CLIENT_H_
2#define SENSESP_SRC_SENSESP_SIGNALK_SIGNALK_WS_CLIENT_H_
3
4#include "sensesp.h"
5
6#include <algorithm>
7
8#include <ArduinoJson.h>
9#include <esp_websocket_client.h>
10#include <functional>
11#include <list>
12#include <set>
13
19#include "sensesp_base_app.h"
20
21namespace sensesp {
22
23static const char* NULL_AUTH_TOKEN = "";
24
31
37 virtual public ValueProducer<SKWSConnectionState> {
38 public:
40 // main task methods
41
42 SKWSClient(const String& config_path,
43 std::shared_ptr<SKDeltaQueue> sk_delta_queue,
44 const String& server_address, uint16_t server_port,
45 bool use_mdns = true);
46
47 const String get_server_address() const { return server_address_; }
48 uint16_t get_server_port() const { return server_port_; }
49
50 virtual bool to_json(JsonObject& root) override final;
51 virtual bool from_json(const JsonObject& config) override final;
52
60
69
70 String get_connection_status();
71
73 // SKWSClient task methods
74
75 void on_disconnected();
76 void on_error();
77 void on_connected();
78 void on_receive_delta(uint8_t* payload, size_t length);
79 void on_receive_updates(JsonDocument& message);
80 void on_receive_put(JsonDocument& message);
81 void connect();
82 void loop();
83 bool is_connected();
84 void restart();
85 bool is_connect_due() const { return millis() >= next_attempt_ms_; }
86 void send_delta();
87
93 void sendTXT(String& payload);
94
98 bool is_ssl_enabled() const { return ssl_enabled_; }
99
106 void set_ssl_enabled(bool enabled) {
107 ssl_enabled_ = enabled;
108 save();
109 }
110
114 bool is_tofu_enabled() const { return tofu_enabled_; }
115
122 void set_tofu_enabled(bool enabled) {
123 tofu_enabled_ = enabled;
124 save();
125 }
126
130 bool has_tofu_fingerprint() const { return !tofu_fingerprint_.isEmpty(); }
131
135 const String& get_tofu_fingerprint() const { return tofu_fingerprint_; }
136
145 save();
146 }
147
151 void set_tofu_fingerprint(const String& fingerprint) {
152 tofu_fingerprint_ = fingerprint;
153 save();
154 }
155
156 protected:
157 // these are the actually used values
158 String server_address_ = "";
159 uint16_t server_port_ = 80;
160 // these are the hardcoded and/or conf file values
162 uint16_t conf_server_port_ = 0;
163 bool use_mdns_ = true;
164
165 String client_id_ = "";
166 String polling_href_ = "";
167 String auth_token_ = NULL_AUTH_TOKEN;
168 bool server_detected_ = false;
170
171 unsigned long next_attempt_ms_ = 0;
172 unsigned long connect_interval_ms_ = 2000;
173
174 // SSL/TLS configuration
175 bool ssl_enabled_ = false;
176 bool tofu_enabled_ = true; // TOFU enabled by default
177 String tofu_fingerprint_ = ""; // SHA256 fingerprint in hex (64 chars)
178
181
186
187 esp_websocket_client_handle_t client_ = nullptr;
188 std::shared_ptr<SKDeltaQueue> sk_delta_queue_;
193
195 SemaphoreHandle_t received_updates_semaphore_ =
196 xSemaphoreCreateRecursiveMutexStatic(&received_updates_semaphore_buffer_);
197 std::list<JsonDocument> received_updates_{};
198
200 // methods for all tasks
201
202 bool take_received_updates_semaphore(unsigned long int timeout_ms = 0) {
203 if (timeout_ms == 0) {
204 return xSemaphoreTakeRecursive(received_updates_semaphore_,
205 portMAX_DELAY) == pdTRUE;
206 } else {
207 return xSemaphoreTakeRecursive(received_updates_semaphore_,
208 timeout_ms) == pdTRUE;
209 }
210 }
212 xSemaphoreGiveRecursive(received_updates_semaphore_);
213 }
214
216 // main task methods
217
219
221 // SKWSClient task methods
222
223 void test_token(const String host, const uint16_t port);
224 void send_access_request(const String host, const uint16_t port);
225 void poll_access_request(const String host, const uint16_t port,
226 const String href);
227 void connect_ws(const String& host, const uint16_t port);
228 void subscribe_listeners();
229 bool get_mdns_service(String& server_address, uint16_t& server_port);
230 bool detect_ssl();
231
237
240 (esp_random() % (connect_interval_ms_ / 4 + 1));
242 std::min(connect_interval_ms_ * 2, (unsigned long)60000);
243 }
244
248};
249
250inline const String ConfigSchema(const SKWSClient& obj) {
251 return "{\"type\":\"object\",\"properties\":{"
252 "\"ssl_enabled\":{\"title\":\"SSL/TLS Enabled\",\"type\":\"boolean\"},"
253 "\"tofu_enabled\":{\"title\":\"TOFU Verification\",\"type\":\"boolean\"},"
254 "\"tofu_fingerprint\":{\"title\":\"Server Fingerprint\",\"type\":\"string\",\"readOnly\":true}"
255 "}}";
256}
257
258inline bool ConfigRequiresRestart(const SKWSClient& obj) { return true; }
259
260} // namespace sensesp
261
262#endif
virtual bool save() override
Save the object to a persistent storage.
Definition saveable.cpp:40
FileSystemSaveable(const String &config_path)
Definition saveable.h:63
Integrator integrates (accumulates) the incoming values.
Definition integrator.h:19
The websocket connection to the Signal K server.
void reset_tofu_fingerprint()
Reset the stored TOFU certificate fingerprint.
std::list< JsonDocument > received_updates_
void poll_access_request(const String host, const uint16_t port, const String href)
SKWSClient(const String &config_path, std::shared_ptr< SKDeltaQueue > sk_delta_queue, const String &server_address, uint16_t server_port, bool use_mdns=true)
void on_receive_delta(uint8_t *payload, size_t length)
Called when the websocket receives a delta.
SemaphoreHandle_t received_updates_semaphore_
unsigned long connect_interval_ms_
void set_tofu_fingerprint(const String &fingerprint)
Set the TOFU fingerprint (called from verify callback).
void process_received_updates()
Loop through the received updates and process them.
void set_ssl_enabled(bool enabled)
Enable or disable SSL/TLS manually.
bool is_connect_due() const
void connect_ws(const String &host, const uint16_t port)
unsigned long next_attempt_ms_
Integrator< int, int > delta_tx_count_producer_
TaskQueueProducer< SKWSConnectionState > connection_state_
SKWSConnectionState get_connection_state()
void on_receive_put(JsonDocument &message)
Called when a PUT event is received.
void release_received_updates_semaphore()
SKWSConnectionState task_connection_state_
void send_access_request(const String host, const uint16_t port)
Integrator< int, int > delta_rx_count_producer_
virtual bool to_json(JsonObject &root) override final
uint16_t get_server_port() const
esp_websocket_client_handle_t client_
void on_error()
Called when the websocket connection encounters an error.
void sendTXT(String &payload)
Send some processed data to the websocket.
bool get_mdns_service(String &server_address, uint16_t &server_port)
void on_disconnected()
Called when the websocket connection is disconnected.
ValueProducer< int > & get_delta_tx_count_producer()
void subscribe_listeners()
Subscribes the SK delta paths to the websocket.
void on_receive_updates(JsonDocument &message)
Called when a delta update is received.
void test_token(const String host, const uint16_t port)
bool has_tofu_fingerprint() const
Check if a TOFU fingerprint is stored.
void set_connection_state(SKWSConnectionState state)
ValueProducer< int > & get_delta_rx_count_producer()
Get the delta rx count producer object.
bool take_received_updates_semaphore(unsigned long int timeout_ms=0)
StaticSemaphore_t received_updates_semaphore_buffer_
virtual bool from_json(const JsonObject &config) override final
void set_tofu_enabled(bool enabled)
Enable or disable TOFU certificate verification.
String get_connection_status()
Get a String representation of the current connection state.
const String get_server_address() const
TaskQueueProducer< int > delta_tx_tick_producer_
Emits the number of deltas sent since last report.
const String & get_tofu_fingerprint() const
Get the stored TOFU fingerprint.
bool is_ssl_enabled() const
Check if SSL/TLS is enabled.
std::shared_ptr< SKDeltaQueue > sk_delta_queue_
void on_connected()
Called when the websocket connection is established.
bool is_tofu_enabled() const
Check if TOFU certificate verification is enabled.
Producer class that works across task boundaries.
const String ConfigSchema(const SmartSwitchController &obj)
std::shared_ptr< reactesp::EventLoop > event_loop()
Definition sensesp.cpp:9
bool ConfigRequiresRestart(const HTTPServer &obj)